Path: AVR_EN => Applications => Model railway crossing gate => Source code V1
Gate ATtiny24 Applications of
AVR Single chip controllers AT90S, ATtiny, ATmega and ATxmega
Servomotor controller with an ATtiny24
Logo

Software for the model railroad crossing gate with an ATtiny24 and a servermotor

The source code of the software for the model railroad crossing is available here.

;
; ***************************************************
; * Railway gate with an ATtiny24 and a servo motor *
; * V1, 19.07.2014, translated 17.03.2018           *
; * (C)2014-2018 by http://www.avr-asm-tutorial.net *
; ***************************************************
;
; Include file for the AVR type
.NOLIST
.INCLUDE "tn24def.inc" ; Header file for ATtiny24
.LIST
;
; Debug switches (final: all zero)
.equ debugparam=0
;
; ============================================
;   H A R D W A R E   I N F O R M A T I O N 
; ============================================
;
;             ______________
;            /   ATtiny24   |
;      +U o--|VCC        GND|--o -U
; Duo LED    |              |
;   Red   o--|PB0       ADC0|--o Lower trim
;   Anode    |              |
;   Red   o--|PB1       ADC1|--o Upper trim
;   Cat      |              |
;   RESET o--|RESET     ADC3|--o Speed trim
;            |              |
;     DWN o--|INT0       PA4|--o NC
;            |              |
;      UP o--|PCINT7    USC0|--o SCK
;            |              |
; PWM-SIG o--|OC1A/MOSI MISO|--o MISO
;  & MOSI    |______________|
;
;
; ============================================
;      P O R T S   A N D   P I N S 
; ============================================
;
; Ports for LED control
.EQU pLedO = PORTB
.EQU pLedI = PINB
.EQU pLedD = DDRB
.EQU bLedPlO = PORTB0
.EQU bLedPlD = DDB0
.EQU bLedPlI = PINB0
.EQU bLedMiO = PORTB1
.EQU bLedMiD = DDB1
.EQU bLedMiI = PINB1
; Ports fuer Tastensteuerung
.EQU pDwnO = PORTB
.EQU pDwnD = DDRB
.EQU bDwn = PORTB2
.EQU pUpO = PORTA
.EQU pUpD = DDRA
.EQU pUpI = PINA
.EQU bUpO = PORTA7
.EQU bUpD = DDA7
.EQU bUpI = PINA7
; Ports fuer PWM-Signal
.EQU pPwmO = PORTA
.EQU pPwmI = PINA
.EQU pPwmD = DDRA
.EQU bPwmO = PORTA6
.EQU bPwmD = DDA6
.EQU bPwmI = PINA6
;
; =============================================
;    C O N S T A N T S   T O   A D J U S T
; =============================================
;
.EQU cMinMin = 900 ; us shortest PWM signal duration
.EQU cMaxMax = 2100 ; us longest PWM signal duration
.EQU cMinMed = 1000 ; us lower regulation limit
.EQU cMaxMed = 2000 ; us upper regulation limit
.EQU cMid = (cMaxMax+cMinMin) / 2 ; Middle position
.EQU cPwm = 20000 ; us total PWM duration
.EQU cSpeedHigh = 1000000 ; us duration fastest closing
.EQU cSpeedLow = 7000000 ; us duration slowest closing
;
; ===============================================
;  F I X E D + D E R I V E D   C O N S T A N T S 
; ===============================================
;
.EQU cMaxMin = cMaxMax - 511 ; Lower limit closing
.EQU cRoundsFast = cSpeedHigh / cPwm
.EQU cRoundsSlow = cSpeedLow / cPwm
.EQU cAdderFast = (cMaxMed - cMinMed) / cRoundsFast
.EQU cAdderSlow = 1 ; Slow adder
.EQU cSpeedMulti = cAdderFast - cAdderSlow
;
; ===================================
;  P R O G R A M   S T R U C T U R E
; ===================================
;
; All timing is based on a clock frequency of 1 MHz,
; which is the default clock frequency with CLKDIV8
; set and the 8 MHz RC oscillator selected.
;
; ADC values
; Measuring the trim potentiometer settings runs with
; an ADC clock prescaler of 128. 64 measurements per
; channel are added up in the registers R0 to R5 and
; averaged.
; From this a measuring frequency of 1 Mhz / 128 /
; 13 clock cycles per conversion / 3 channels / 64
; single measurements = approx. 3 Hz results.
; Calculation of those measurements goes as follows:
; Trim 1: Sum/128+cMinMin(900) ==> rClose (Minimum)
; Trim 2: cMaxMax(2100)-Sum/128 ==> rOpen (Maximum)
; Trim 3: (Sum/256*cSpeedMulti)/256+1 ==> rDelta
;   (Speed), values vary between 1 and 23
;   (1 = 20 seconds closure time, 23 = one second
;   closure time
;
; Timer1: Generation of the PWM signal
;   TC1 is clocked by the clock signal and a prescaler
;   of 1, one tick is therefore 1 us.
;   TC1 runs as CTC and generates the PWM signal on the
;   output pin OC1A on PA6. The output pin toggles on
;   compare match A, TC1 restarts and triggers a
;   compare match A intterupt. Depending from the state
;   of the output pin either the duration in rCtcAct or
;   in rCtcIna is written to the compare register A. The
;   resulting looks as follows:
;
;       _______                   _______                   ______
; _____|       |_________________|       |_________________|
;       rCtcAct    rCtcIna        rCtcAct      rCtcIna
;      |<======= 20.000 us =====>|<======= 20.000 us =====>|
;
; rCtcAct = 900 .. 2.100 us
;   Lower limit adjustable with trim 1 between 900 and 1410 us
;   Upper limit adjustable with trim 2 between 1590 and 2100 us
; rCtcIna = 20.000 - rCtcAct
;
; rCtcAct plus rCtcIna always yields 20.000 us, a 50 Hz signal.
; When the OC1A pin is set (after writing rCtcIna) the bPwm flag
; is set, which triggers execution of the routine PwmRdy. This
; determines if the gate is currently moving or not.
; If this is not the case, 25 following PWM signal bursts are
; sent with the current position (0.5 seconds). After that the
; PWM signal output is cleared (by clearing the output pin on
; compare match).
; If the gate is moving the duration of the PWM signal is increased
; by the content of rDelta, if the gate opens, or decreased if
; the gate closes. If, on opening, the upper limit in rOpen is
; exceeded the LED is switched to permanently green and the
; post-PWM period is initiated.
; If, on closing, the lower limit in rClose is reached the LED
; is switched to permanent red and the post-PWM period initiated.
; Bei aktiver Schranke wird die Dauer des PWM-Signals um den Betrag
; rDelta verlaengert (Schranke oeffnet sich) bzw. verkuerzt (Schranke
; schliesst sich). Ist beim Oeffnen die obere Grenze in rOben ueber-
; schritten wird die LED auf gruenes Dauerlicht geschaltet und der
; Nachlauf eingeleitet. Ist beim Schliessen die untere Grenze unter-
; schritten wird die LED auf rotes Dauerlicht geschaltet und der
; Nachlauf eingeleitet.
; If neither the upper nor the lower limit has been reached the
; LED blinks with 0.2 Hz.
;
; Triggering opening and closing of the gate
; If push button 1 is activated, an INT0 interrupt is triggered.
; This clears the upward flag.
; If push button 2 is activated, the PCINT0 interrupt is triggered.
; Following both interrupts and if the toggle bit of TC1 is not
; set, the rCtcIna value is written to the compare A port, the
; toggle mode in TC1 is set and the compare match A interrupt is
; enabled. Finally the "Gate active" flag is set.
;
; Adjusting
; If the value of the up- or down-trim-poentiometer is changed,
; the new value is immediately applied. If the gate is inactive
; (bActive flag cleared), the gate only moves if it is completely
; open (last movement upwards) or completely closed (last movement
; downwards).
; Changes of the speed trim potentiometer come only into effect
; if the gate moves.
;
; Start conditions
; On start up the gate is moved to to its middle position and then
; upwards to the upper limit.
;
; ============================================
;   R E G I S T E R   D E F I N I T I O N S
; ============================================
;
; R0 .. R5 : ADC sum values, three channels
.DEF rCtcActL = R6 ; Active signal duration, us
.DEF rCtcActH = R7
.DEF rCtcInaL = R8 ; Inactive signal duration, us
.DEF rCtcInaH = R9
.DEF rCloseL = R10 ; Lower limit PWM signal
.DEF rCloseH = R11
.DEF rOpenL = R12 ; Upper limit PWM signal
.DEF rOpenH = R13
.DEF rDelta = R14 ; Step length for gate movement
.DEF rSreg = R15 ; for SREG interim storage in ints
.DEF rmp = R16 ; Multi purpose register
.DEF rimp = R17 ; Multi purpose inside interrupts
.DEF rimp2 = R18 ; 16 bit adder register in interrupts
.DEF rAdcCnt = R19 ; Counter for ADC measurements
.DEF rFlg = R20 ; Flag register
	.equ bActive = 0 ; Gate is moving
	.equ bHigh = 1 ; Gate is moving up
	.equ bAdc = 2 ; ADC sequence complete
	.equ bPwmIna = 3 ; Inactive PWM signal, handle flag
	.equ bOpen = 4 ; Gate is open
	.equ bClose = 5 ; Gate is closed
	.equ bToggle = 6 ; CTC toggle is active
.DEF rPwmCtr = R21 ; Downcounter for post-PWM phase
; R22 .. R25 not used
; X = XH:XL Multiplication for speed adjustment
; Y = YH:YL ADC pointer to SRAM
; Z = ZH:ZL Multi purpose register pair outside interrupts
;
; ============================================
;       S R A M   D E F I N I T I O N S
; ============================================
;
.DSEG
.ORG  0X0060
sAdc: .Byte 6
;
; ==============================================
;   R E S E T   A N D   I N T   V E C T O R E N
; ==============================================
;
.CSEG
.ORG $0000
	rjmp Main ; Reset-Vector
	rjmp IntDwn ; Int Vector INT0
	rjmp IntUp ; Int Vector PCINT0
	reti ; Int Vector PCINT1
	reti ; Int Vector WDT
	reti ; Int Vector TC1 Capture
	rjmp IntPwmA ; Int Vector TC1 Compare A
	reti ; Int Vector TC1 Compare B
	reti ; Int Vector TC1 OVF
	reti ; Int Vector TC0 Compare A
	reti ; Int Vector TC0 Compare B
	reti ; Int Vector TC0 OVF
	reti ; Int Vector Ana_Comp
	rjmp IntAdc ; Int Vector ADC
	reti ; Int Vector EEPROM Ready
	reti ; Int Vector USI START
	reti ; Int Vector USI Overflow
;
; ==========================================
;    I N T E R R U P T   S E R V I C E
; ==========================================
;
; Downwards push button
IntDwn:
	in rSreg,SREG ; Save SREG
	cbr rFlg,1<<bHigh ; Clear upward flag
	rjmp IntUD
;
; Upwards push button
IntUp:
	in rSreg,SREG ; Save SREG
	sbic pUpI,bUpI ; Falling edge?
	reti ; No, ignore
	sbr rFlg,1<<bHigh ; Set upwards flag
IntUD:
	; Start move cycle
	sbrc rFlg,bActive ; Already active?
	rjmp IntUD1 ; Yes, ignore
	sbr rFlg,1<<bActive ; Set gate aktice flag
	sbrc rFlg,bToggle ; Skip if not in toggle mode
	rjmp IntUD1 ; Toggle-Mode is on
	ldi rPwmCtr,1 ; Start blink counter
	out OCR1AH,rCtcInaH ; Compare match inactive duration
	out OCR1AL,rCtcInaL
	ldi rimp,1<<COM1A0 ; Enable toggle of output pin
	out TCCR1A,rimp
	ldi rimp,1<<OCIE1A ; Enable TC1COMPA interrupt
	out TIMSK1,rimp
	sbr rFlg,1<<bToggle
IntUD1:
	out SREG,rSreg ; Restore SREG
	reti
;
; TC1 Compare match A interrupt service routine
IntPwmA:
	in rSreg,SREG ; Save SREG
	sbic pPwmI,bPwmI ; Skip if PWM signal=0
	rjmp IntPwmA1
	out OCR1AH,rCtcInaH ; Long inactive pause
	out OCR1AL,rCtcInaL
	sbr rFlg,1<<bPwmIna ; Set flag
	rjmp IntPwmA2
IntPwmA1:
	out OCR1AH,rCtcActH ; Short active signal
	out OCR1AL,rCtcActL
IntPwmA2: 
	out SREG,rSreg ; Restore SREG
	reti
;
; AD conversion complete interrupt service routine
IntAdc:
	in rSreg,SREG ; Save SREG
	in rimp,ADCL ; Read LSB result
	ld rimp2,Y ; Read current sum LSB
	add rimp,rimp2 ; Add LSB to current
	st Y+,rimp ; and store in SRAM
	in rimp,ADCH ; Read MSB result
	ld rimp2,Y ; Read MSB current sum
	adc rimp,rimp2 ; Add MSBs with carry
	st Y+,rimp ; Store MSB
	cpi YL,6 ; End of round?
	brcs IntAdcRet ; No, continue
	clr YL ; Restart
	dec rAdcCnt ; Count rounds down
	brne IntAdcRet
AdcCopy:
	; End of ADC measuring cycle
	ldi YL,LOW(sAdc) ; Copy to SRAM
	st Y+,R0
	st Y+,R1
	st Y+,R2
	st Y+,R3
	st Y+,R4
	st Y,R5
	clr YL
	clr R0 ; Clear sums 
	clr R1
	clr R2
	clr R3
	clr R4
	clr R5
	sbr rFlg,1<<bAdc ; Set flag
IntAdcRet:
	mov rimp,YL ; Copy MUX channel
	lsr rimp ; Divide by two
	out ADMUX,rimp ; MUX to next channel
	; Start next conversion
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp
	out SREG,rSreg ; Restore SREG
	reti
;
; ============================================
;    M A I N   P R O G R A M   I N I T
; ============================================
;
Main:
; Init stack
	ldi rmp, LOW(RAMEND) ; Init LSB stack
	out SPL,rmp
; Init port bits
	; Init LED pins
	sbi pLedD,bLedPlD ; LED pins as output
	sbi pLedD,bLedMiD
	sbi pLedO,bLedPlO ; Turn red LED on
	cbi pLedO,bLedMiO
	; Init the push button pins
	cbi pDwnD,bDwn ; Close push button as input
	sbi pDwnO,bDwn ; Pull up resistor on
	cbi pUpD,bUpD ; Close push button as input
	sbi pUpO,bUpO ; Pull up resistor on
	; Init PWM output pin
	sbi pPwmD,bPwmD ; PWM output pin as output
	cbi pPwmO,bPwmO ; To low
; Init ADC
	; Single measurement at start up
.if debugparam==1
	rjmp debugcalc
	nop
	.endif 
	rcall AdcGet ; Get first cycle
	ldi rmp,HIGH(cMid) ; Position to middle
	mov rCtcActH,rmp
	ldi rmp,LOW(cMid)
	mov rCtcActL,rmp
	clr rFlg ; Clear flags
	sbr rFlg,(1<<bActive)|(1<<bHigh) ; Move gate up
	ldi rPwmCtr,1 ; Init LED blink counter
	rcall PwmRdyO ; Blink and correct Inactive duration
	; Prepare ADC sequence
	ldi YH,HIGH(sAdc) ; Pointer to SRAM
	ldi YL,LOW(sAdc)
	ldi rmp,0 ; MUX to channel 0
	out ADMUX,rmp
	ldi rmp,(1<<ADC0D)|(1<<ADC1D)|(1<<ADC0D) ; Digital input driver disable
	out DIDR0,rmp
	clr rmp ; Results not left adjust
	out ADCSRB,rmp
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
; Init timer 1
	out OCR1AH,rCtcInaH ; CTC to inactive duration
	out OCR1AL,rCtcInaL
	ldi rmp,1<<COM1A0 ; Toggle output A on compare match
	out TCCR1A,rmp
	ldi rmp,(1<<WGM12)|(1<<CS10) ; CTC mode, prescaler = 1
	out TCCR1B,rmp
	ldi rmp,1<<OCIE1A ; TC1 Compare Match A Interrupt Enable
	out TIMSK1,rmp
	sbr rFlg,1<<bToggle ; Toggle is active
; Init PCINT7
	ldi rmp,1<<PCINT7 ; Pin-Change Int push button 2
	out PCMSK0,rmp
; Init external interrupts and Sleep mode idle
	ldi rmp,(1<<SE)|(1<<ISC01) ; Enable sleep and INT0
	out MCUCR,rmp
	; External interrupts
	ldi rmp,(1<<INT0)|(1<<PCIE0) ; Enable INT0 and PCINT0 interrupts
	out GIMSK,rmp
	sei ; Enable interrupts
;
; ============================================
;        P R O G R A M - L O O P
; ============================================
;
Loop:
	sleep ; Send to sleep
	nop ; Dummy for wake-up
	sbrc rFlg,bPwmIna ; PWM flag set?
	rcall PwmRdy ; Yes, handle PWM
	sbrc rFlg,bAdc ; ADC flag set?
	rcall AdcRdy ; Yes, handle ADC
	rjmp loop ; Back to loop
;
; PWM ready
PwmRdy:
	cbr rFlg,1<<bPwmIna ; Clear flag
	sbrc rFlg,bActive ; Skip if gate inactive
	rjmp PwmRdyA ; Gate active
	; Gate inactive
	sbrs rFlg,bToggle ; Skip if toggle is on
	ret ; Toggle is off, return
	; TC1 toggle is on
	dec rPwmCtr ; Count post-PWM down
	brne PwmRdy1 ; Not finished yet
	ldi rmp,1<<COM1A1 ; Set TC output pin to clear
	out TCCR1A,rmp ; Switch toggle off
	clr rmp ; Disable TC1 interrupt
	out TIMSK1,rmp
	cbr rFlg,1<<bToggle ; Toggle flag off
PwmRdy1:
	ret
PwmRdyA: ; Gate ist active
	sbrc rFlg,bHigh ; Skip if gate is closed
	rjmp PwmRdyH ; Gate upwards
	; Gate downwards
	sub rCtcActL,rDelta ; Reduce PWM signal duration
	brcc PwmRdyA1 ; No carry
	dec rCtcActH ; MSB downwards
PwmRdyA1:
	cp rCtcActL,rCloseL ; Compare with lower limit
	cpc rCtcActH,rCloseH
	brcc PwmRdyA2 ; Not yet reached
	mov rCtcActL,rCloseL ; Reached, set close value
	mov rCtcActH,rCloseH
	cbr rFlg,1<<bActive ; End gate movement
	sbr rFlg,1<<bClose ; Set close flag
	sbi pLedO,bLedPlO ; Red LED permanently on
	cbi pLedO,bLedMiO
	ldi rPwmCtr,25 ; Start repeat counter for post-PWM
	rjmp PwmRdyK ; Correct duration PWM
	; Not yet on close position
PwmRdyA2:
	cbr rFlg,1<<bClose ; Close flag off
	rjmp PwmRdyO ; Blink and correct PWM duration
PwmRdyH:
	; Gate upwards
	add rCtcActL,rDelta ; Add delta to signal duration
	brcc PwmRdyH1 ; No carry
	inc rCtcActH ; Carry to MSB
PwmRdyH1:
	cp rCtcActL,rOpenL ; Compare with upper limit
	cpc rCtcActH,rOpenH
	brcs PwmRdyH2 ; Not yet reached
	mov rCtcActL,rOpenL ; Reached, set upper limit
	mov rCtcActH,rOpenH
	cbr rFlg,1<<bActive ; End gate movement
	sbr rFlg,1<<bOpen ; Gate open flag
	cbi pLedO,bLedPlO ; LED permanently green
	sbi pLedO,bLedMiO
	ldi rPwmCtr,25 ; Start repeat counter for post-PWM
	rjmp PwmRdyK ; Correct duration of PWM inactive
PwmRdyH2:
	cbr rFlg,1<<bOpen ; Open flag off
	rjmp PwmRdyO ; LED blink and CTC-Inactive correction 
PwmRdyO:
	; LED blink
	cbi pLedO,bLedMiO
	dec rPwmCtr ; Counter for blink LED
	brne PwmRdyK ; Correct inactive duration PWM
	ldi rPwmCtr,12 ; Restart counter
	sbic pLedI,bLedPlI ; Skip if LED is off
	rjmp PwmRdyO1 ; LED is on
	sbi pLedO,bLedPlO ; LED on
	rjmp PwmRdyK
PwmRdyO1:
	cbi pLedO,bLedPlO ; LED off
PwmRdyK:
	; Correct inactive duration PWM
	ldi rmp,LOW(cPwm) ; Total duration - Active duration
	sub rmp,rCtcActL
	mov rCtcInaL,rmp ; Copy to inactive duration
	ldi rmp,HIGH(cPwm)
	sbc rmp,rCtcActH
	mov rCtcInaH,rmp
	ret
;
; ADC measuring sequence ended
;
AdcRdy:
	cbr rFlg,1<<bAdc
	push rCloseL
	push rCloseH
	push rOpenL
	push rOpenH
	rcall CalcSpeed
	pop XH
	pop XL
	pop ZH
	pop ZL
	sbrc rFlg,bActive ; Skip if gate still active
	ret ; Yes
	sbrc rFlg,bToggle ; Skip if toggle active
	ret ; Yes
	ldi rmp,LOW(cMid) ; Below or above middle?
	cp rCtcActL,rmp
	ldi rmp,HIGH(cMid)
	cpc rCtcActH,rmp
	brcc AdcRdyU 
	; Gate is below middle
	mov rCtcActH,rCloseH
	mov rCtcActL,rCloseL
	rjmp AdcRdyS ; Start move cycle
AdcRdyU:
	; Gate is above middle
	mov rCtcActH,rOpenH
	mov rCtcActL,rOpenL
AdcRdyS:
	; Start adjustment
	rcall PwmRdyK ; Calculate inactive duration
	ldi rPwmCtr,10 ; Start post-PWM cycle
	ldi rmp,1<<COM1A0 ; Start toggle
	out TCCR1A,rmp
	ldi rmp,1<<OCIE1A ; Start interrupt
	out TIMSK1,rmp
	sbr rFlg,1<<bToggle ; Set toggle flank
	ret
;
; =============================================
;     A S Y N C H   S U B R O U T I N E S
; =============================================
;
; Get ADC results during init
AdcGet:
	ldi rmp,(1<<ADC0D)|(1<<ADC1D)|(1<<ADC2D) ; Digital drivers off
	out DIDR0,rmp
	clr rmp ; Result not left adjusted
	out ADCSRB,rmp
	ldi YH,HIGH(sAdc) ; Channel counter Y points to SRAM
	ldi YL,LOW(sAdc)
GetAdc1:
	clr R0 ; Clear result
	clr R1
	ldi rAdcCnt,64 ; 64 measurements
	mov rmp,YL ; MUX channel
	lsr rmp
	andi rmp,0x03 ; Isolate channel bits 
	out ADMUX,rmp
GetAdc2:
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
GetAdc3:
	sbic ADCSRA,ADSC ; Wait for result
	rjmp GetAdc3
	in rmp,ADCL ; Add LSB to result
	add R0,rmp
	in rmp,ADCH ; Add MSB to result
	adc R1,rmp
	dec rAdcCnt
	brne GetAdc2 ; Again convert
	st Y+,R0 ; Store LSB
	st Y+,R1 ; Store MSB
	cpi YL,sAdc+6 ; All three channels read?
	brcs GetAdc1
;
; Calculate delta from speed position
CalcSpeed:
	lds rmp,sAdc+5 ; ADC result MSB channel 3
	clr XH ; Multiplicator MSB
	ldi XL,cSpeedMulti ; LSB = Multiplicator for speed
	clr ZH ; Z is result
	clr ZL
CalcSpeed1:
	lsr rmp ; Bit = 1?
	brcc CalcSpeed2 ; No, do not add
	add ZL,XL ; Add LSB multiplicator
	adc ZH,XH ; Add MSB and carry
CalcSpeed2:
	lsl XL ; Double multiplicator
	rol XH
	tst rmp ; Already done?
	brne CalcSpeed1 ; No, further bits
	ldi rmp,cAdderSlow
	add rmp,ZH ; Add MSB of the result
	mov rDelta,rmp
;
; Lower and upper trim potentiometer
AdcConv:
	lds rmp,sAdc ; Read LSB lower trim
	lds ZL,sAdc+1 ; Read MSB upper trim
	clr ZH
	lsl rmp ; Upper bit LSB into lower bit LSB
	rol ZL ; Rotate into LSB
	rol ZH ; And into MSB
	ldi rmp,LOW(cMinMin) ; Minimum duration lower limit
	add ZL,rmp ; Add to result
	ldi rmp,HIGH(cMinMin)
	adc ZH,rmp
	andi ZL,0xFC ; Clear lowest two bits
	mov rCloseH,ZH ; Copy to closure position
	mov rCloseL,ZL
	lds rmp,sAdc+2 ; Read LSB upper trim
	lds ZL,sAdc+3 ; Read MSB upper trim
	clr ZH
	lsl rmp ; Upper bit LSB to lowest bit LSB
	rol ZL ; Rotate bit into LSB in ZL
	rol ZH ; And into MSB
	ldi rmp,LOW(cMaxMin) ; LSB maximum duration upper position
	add ZL,rmp ; Add to LSB
	ldi rmp,HIGH(cMaxMin)
	adc ZH,rmp ; Add MSB and carry
	andi ZL,0xFC ; Clear lowest two bits
	mov rOpenL,ZL
	mov rOpenH,ZH
	ret
.if debugparam==1
debugcalc:
	ldi rmp,0xFF
	ldi ZH,HIGH(sAdc)
	ldi ZL,LOW(sAdc)
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	rjmp CalcSpeed
	.endif
;
; End of source code
; Copyright
.db "(C)2014 by Gerhard Schmidt  " ; human readable
.db "C(2)10 4ybG reahdrS hcimtd  " ; wordwise
;

Top of page


©2014 by http://www.avr-asm-tutorial.net