![]() |
Applications of AVR Single chip controllers AT90S, ATtiny, ATmega and ATxmega Servomotor controller with an ATtiny24 |
![]() |
;
; ***********************************
; * Gate controller with ATtiny24 *
; * (C)2018 by avr-asm-tutorial.net *
; ***********************************
;
.nolist
.include "tn24adef.inc" ; Define device ATtiny24A
.list
;
; **********************************
; D E B U G S W I T C H E S
; **********************************
;
; All switches have to be 0 in the final version
;
; Test the ADC conversion routines
.equ DebugAdc = 0 ; Debug the ADC routines
.equ cAdcChannel = 1 ; Channel (0..2)
.equ cAdcResult = 65535 ; Result of the ADC sum, 0 to 6535
.equ cOpen = 1000 ; Open position, 800 to 1,500
.equ cClose = 2000 ; Close position, 1,500 to 2,200
.equ DebugPosition = 0 ; Follow the current position
.equ DebugOpen = 0 ; Choose this to follow the open position
.equ DebugClose = 0 ; Choose this to follow the close position
.equ DebugOpenClose = 0 ; Open and close the gate
;
; Check errors
.if (DebugPosition == 1) && (DebugOpen == 0) && (DebugClose == 0)
.error "No positioning selected!"
.endif
.if (DebugPosition == 1) && (DebugOpenClose == 1)
.error "Positioning and Open/Close settings conflicting!"
.endif
;
; **********************************
; H A R D W A R E
; **********************************
;
; Device: ATtiny24A, Package: 14-pin-PDIP_SOIC
;
; _________
; 1 / |14
; + 5 V o--|VCC GND|--o 0 V
; DuoLED 2| |13
; An rd o--|PB0 ADC0|--o Trim 1
; 3| |12
; An gn o--|PB1 ADC1|--o Trim 2
; 4| |11
; RESET o--|RESET ADC2|--o Trim 3
; 5| |10
; BtnUp o--|INT0 PA3|--o NC
; 6| |9
; BtnDwno--|PCINT7 SCK|--o ISP6-SCK
; 7| |8
; PWM o--|OC1A MOSI|--o ISP6-MOSI
; ISP6-MISO|__________|
;
;
; **********************************
; P O R T S A N D P I N S
; **********************************
; Duo LED control
.equ pLedO = PORTB ; LED output port
.equ pLedD = DDRB ; LED direction port
.equ pLedI = PINB ; LED input port
.equ bLedAnRdO = PORTB0 ; Red anode output pin
.equ bLedAnRdD = DDB0 ; Red anode direction pin
.equ bLedAnGnO = PORTB1 ; Green anode output pin
.equ bLedAnGnD = DDB1 ; Green anode direction pin
;
; Push button down control
.equ pBtnDO = PORTB ; Push button up output port
.equ pBtnDD = DDRB ; Push button up direction port
.equ pBtnDI = PINB ; Push button up input port
.equ bBtnDO = PORTB2 ; Push button up output pin
.equ bBtnDD = DDB2 ; Push button up direction pin
.equ bBtnDI = PINB2 ; Push button up input pin
;
; Push button up control
.equ pBtnUO = PORTA ; Push button down output port
.equ pBtnUD = DDRA ; Push button down direction port
.equ pBtnUI = PINA ; Push button down input port
.equ bBtnUO = PORTA7 ; Push button down output pin
.equ bBtnUD = DDA7 ; Push button down direction pin
.equ bBtnUI = PINA7 ; Push button down input pin
;
; PWM signal
.equ pPwmO = PORTA ; PWM output port
.equ pPwmD = DDRA ; PWM direction port
.equ bPwmO = PORTA6 ; PWM output pin
.equ bPwmD = DDA6 ; PWM direction pin
;
; **********************************
; A D J U S T A B L E C O N S T
; **********************************
;
; PWM signal basic properties
.equ cPwmMin = 1000 ; Mimimum duration, ms
.equ cPwmMax = 2500 ; Maximum duration, ms
.equ cFpwm = 50 ; Frequency of the PWM signal
.equ cPost = 25 ; Post period duration, 0.5 sec
; Up/down movement speed
.equ cSpeedHigh = 1 ; Maximum speed, one second
.equ cSpeedLow = 7 ; Minimum speed, seven seconds
;
; **********************************
; F I X & D E R I V. C O N S T
; **********************************
;
; Controller clock = Default
.equ clock=1000000 ; Define clock frequency
;
; PWM signal duration, default = 20,000 us
.equ cPwm = clock / cFpwm - 1 ; in us
;
; PWM signal properties derived
; Middle position of the servomotor
; Default: 1,500
.equ cPwmMid = (cPwmMax + cPwmMin + 1) / 2
; Max. +/- for up and down movement
; Default: +/- 500
.equ cPwmDelta = (cPwmMax - cPwmMin +1) / 2
; Default values for startup
.equ cPwmCloseDef = cPwmMid - 50 ; slightly down
.equ cPwmOpenDef = cPwmMid + 50 ; slightly up
.equ cPwmSpeedDef = 1 ; Default speed at startup
;
; Speed calculation from trim 3
.equ cCycleHigh = cFpwm*cSpeedHigh ; Default: 50
.equ cCycleLow = cFpwm*cSpeedLow ; Default: 350
.equ cSpdMul = cCycleLow - cCycleHigh ; Multiplicator
.equ cSpdAdd = cCycleHigh ; Adder to result
;
; *********************************************
; H O W T H E S O F T W A R E W O R K S
; *********************************************
;
; Relevant registers:
; rClose: 16 bit register that holds the PWM
; duration of the motor in closed position,
; can be between 1,500 and 800.
; rOpen: 16 bit register that holds the PWM
; duration of the motor in opened position,
; can be between 1,500 and 2,200.
; rDelta: 8 bit register by which the position
; value is increased (closing selected) or
; decreased (opening selected), values are
; between 1 (slowest) and 28 (fastest).
;
; PWM signal and motor position
; The short PWM period (800 us) moves the servo
; to the rightmost position (gate fully closed).
; The long PWM period (2,200 us) moves the servo
; to the leftmost position (gate fully opened).
; Trim 1 increases the closed position from the
; middle position (1,500 us).
; The trim potentiometer P1 (closure) varies the
; PWM period between 1,500 (trim 1 at 0 degrees)
; and 800 us (trim 1 at 270 degrees). The closure
; value is in rClose.
; The trim potentiometer P2 (opening) varies the
; PWM period between 1,500 (trim 2 at 0 degrees)
; and 2,200 us (trim 2 at 270 degrees). The open
; value is in rOpen.
;
; PWM signal generation:
; 16 bit timer TC1 operates as fast PWM generator
; in mode #14. It runs with a prescaler of 1
; (at 1 us per tick). The ICR1 register is used
; to clear the TC1 and to restart the PWM cycle.
; Those two port registers ICR1H:ICR1L are set
; to 19,999 to achieve a 50 Hz PWM frequency as
; required by the servomotor.
; Output pin OC1A (on Pin PA6) is set on PWM
; cycle start and cleared on compare match A (non-
; inverted PWM mode). Compare match A is set to
; the current motor position in us. Its value
; comes only into effect when the end of the PWM
; cycle is reached (TC1 count = 20.000).
; Also at compare match A the next position of
; the motor is calculated (see next para).
;
; TC1 compare match interrupt:
; Moving the gate downwards:
; If the direction of the gate is currently
; downwards (T flag in SREG is cleared), the
; actual compare match A value is compared
; with the rClose register pair. If both are
; equal, the post-period part is executed
; (see below).
; If the position in the compare match value
; A differs from the rClose register pair
; the current position in compare match A
; is reduced by the register rDelta. If the
; result is smaller than the rClose value, the
; rClose value is written to the compare match
; A port registers. If not, the result of the
; subtraction is written as new compare match
; A value. The update of the compare match A
; value occurs automatically on the end of
; the current PWM cycle.
; The post-period counter is set to its
; initial value cPost. The LED is set to
; blink in red color.
;
; Moving the gate upwards:
; If the direction of the gate is currently
; upwards (T flag in SREG is high), the actual
; compare match A value is compared with the
; value in the rOpen register pair. If both
; are equal, post-period execution follows
; (see below).
; If the compare match value A differs from
; rOpen, the rDelta register content is
; added to the compare match A value. If the
; sum exceeds or equals the rOpen value, the
; rOpen value is written to the compare
; match A port registers. If not, the sum is
; written to the compare match A port
; registers. The altered compare match A value
; is coming into effect when the current PWM
; cycle ends.
; The post-period counter is set to its
; initial value cPost. The LED is set to
; blink in red color.
;
; Post-period execution (compare match equals
; rOpen/rClose):
; If either the compare match A value equals
; rOpen in downward direction (T flag is clear)
; or if it equals rClose in downwards direction
; (T flag is clear) the register content of
; rPost is checked. If that is already at
; zero, nothing else happens.
; If not, it is counted down. If it then
; reaches zero, the LED is set permanently
; to green (upwards, T flag set, gate fully
; open), otherwise permanently to red (down-
; wards, T flag cleared, gate fully closed).
;
; Measuring and converting the trim potentiometers:
; Close position trim (P1):
; 64 measurements of ADC channel 0 are summed
; up. The MSB of the result is multiplied by
; cPwmDelta (default: 500), which is a 16-by-8
; bit multiplication). Byte 3 and byte 2 of
; the result are subtracted from cPwmMid (by
; default 1,500) and the result is copied to
; rClose.
;
; Open position trim (P2):
; Channel 1 of the ADC is measured 64 times and
; summed up, the MSB of the result is multi-
; plied with cPwmDelta (700). Byte 3 and byte
; 2 of the result are added to cPwmMid (1,500)
; and the result is written to the register
; pair rOpen.
;
; Speed adjust trim (P3):
; Channel 3 of the ADC is measured 64 times
; and summed up. The MSB is inverted (two's
; complement), multiplied by cSpdMul (by
; default 300 and cSpdAdd (by default 50)
; is added.
; The difference between the rClose and rOpen
; (rClose - rOpen) is divided by byte 3 and
; byte 2 of the result (16-by16-bit division).
; If the 8 bit result is zero, it is set to
; one. This is copied to register rDelta.
;
; Settings at start-up:
; 1. The stack pointer is set to RAMEND.
; 2. If any debug switches have been set,
; those are executed.
; 3. If the push button close is pressed,
; the T flag is cleared otherwise set.
; 4. rOpen is set to 1,550, rClose to
; 1.450 and rDelta to 1 (slow motion).
; 5. The counter TC1 is started in fast
; PWM mode with ICR as CTC, with the
; middle position cPwmMid (by default
; 1,500) as compare match A value, with
; setting the OC1A output pin and clea-
; ring OC1A on compare match and with
; Compare Match A interrupts enabled.
; 6. The ADC is started with ADC0 as
; first channel, with the operating
; voltage as reference, without ADLAR
; and with ADC interrupts enabled.
; Sum registers are cleared and the
; number of measuring cycles is set
; to 64. On the three ADC channels
; the digital drivers are disabled.
; 7. The output pina of the LED are set
; as outputs, the LED is switched off.
; 8. The push button input pins are con-
; figured as input pins and the pull-
; up resistors are activted. On the
; INT0 input interrupts on falling
; edges are enabled. On the PCINT7
; input the mask bit 7 is set and
; the PCINT0 interrupt is enabled.
; 9. Interrupts are enabled and sleep
; mode idle is activated.
;
; Interrupt processing:
; The controller is sent to sleep. All
; further action is executed on interrupt
; events and after waking up the control-
; ler following interrupt service execu-
; tion:
; - Compare match A interrupts set the
; bTC1 flag, after wake-up this triggers
; executing gate movements and operate
; LED properties as described above.
; - External interrupts INT0 and PCINT0
; change the T flag accordingly (PCINT0
; only on falling edges).
; - ADC ready interrupts add the result
; to the sum and restart the conversion,
; if less than 64 measurements on the
; same channel have been performed.
; Otherwise the bAdc flag is set and
; further processing of the sum result
; is performed after wake-up.
;
; **********************************
; R E G I S T E R S
; **********************************
;
; Used: R2:R1:R0 as 24 bit for Multipl./Div.
; Used: R5:R4:R3 as 24 bit for Multipl./Div.
; Free: R6 to R9
.def rBlink = R10 ; Blink counter
.def rPost = R11 ; Post period until inactive
.def rDelta = R12 ; Delta speed to add/subtract
.def rAdcL = R13 ; ADC sum LSB
.def rAdcH = R14 ; dto., MSB
.def rSreg = R15 ; Save/Restore status port
.def rmp = R16 ; Define multipurpose register
.def rimp = R17 ; Define multipurpose inside ints
.def rFlag = R18 ; Flag register
.equ bAdc = 0 ; ADC conversion complete flag
.equ bTC1 = 1 ; TC1 compare match int occurred
.def rAdcC = R19 ; ADC counter
.def rOpenL = R20 ; Open position, us
.def rOpenH = R21
.def rCloseL = R22 ; Close position, us
.def rCloseH = R23
; free: R24 to R29
; used: R31:R30 = Z for diverse purposes outside ints
;
; **********************************
; S R A M
; **********************************
;
.dseg
.org SRAM_START
; (SRAM used only for stack)
;
; **********************************
; C O D E - S E G M E N T
; **********************************
;
.cseg
.org 000000
;
; **********************************
; R E S E T & I N T - V E C T O R S
; **********************************
rjmp Main ; Reset vector
rjmp Int0Isr ; EXT_INT0, push button close gate
rjmp Pcint0Isr ; PCI0, push button open gate
reti ; PCI1, unused
reti ; WATCHDOG, unused
reti ; ICP1, unused
rjmp OC1AIsr ; OC1A
reti ; OC1B, unused
reti ; TC1OVF, unused
reti ; OC0A, unused
reti ; OC0B, unused
reti ; OVF0, unused
reti ; ACI, unused
rjmp AdcIsr ; ADCC
reti ; ERDY, unused
reti ; USI_STR, unused
reti ; USI_OVF, unused
;
; **********************************
; I N T - S E R V I C E R O U T .
; **********************************
;
; INT0 interrupt
; Falling edge on push button close input
; move gate downwards
Int0Isr:
clt ; Downward direction, close gate
reti
;
; PCINT0 interrupt
; Any change on the PCINT7 push button up input
Pcint0Isr:
sbis pBtnUI,bBtnUI ; Skip if bush button up input is high
set ; Upward direction, open gate
reti
;
; OC1AIsr Compare Match A Int
; Occurs once in every PWM cycle (default: 20 ms)
; after compare match A has occurred, triggers
; position control of the gate (movement, post
; period and LED control)
OC1AIsr:
in rSreg,SREG ; Save SREG
sbr rFlag,1<<bTC1 ; Set flag
out SREG,rSreg ; Restore SREG
reti
;
; ADC interrupt
; AD conversion complete, add result,
; count measurements down and restart
; next conversion or set bAdc flag
AdcIsr:
in rSreg,SREG ; Save SREG
in rimp,ADCL ; Read LSB result
add rAdcL,rimp ; Add to sum LSB
in rimp,ADCH ; Read MSB result
adc rAdcH,rimp ; Add to sum MSB with carry
dec rAdcC ; Decrease counter
breq AdcIsrEnd ; Channel cycle ended
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
out ADCSRA,rimp ; Start next conversion
out SREG,rSreg ; Restore SREG
reti
AdcIsrEnd:
sbr rFlag,1<<bAdc ; Set ADC flag
out SREG,rSreg ; Restore SREG
reti
;
; **********************************
; M A I N P R O G R A M I N I T
; **********************************
;
Main:
; Init the stack for interrupts/calls
ldi rmp,Low(RAMEND)
out SPL,rmp ; Init LSB stack pointer
; Debug switches
.if DebugAdc == 1 ; Debug the ADC routines
ldi rmp,cAdcChannel ; Channel (0..2)
out ADMUX,rmp
ldi rmp,High(cAdcResult)
mov rAdcH,rmp
ldi rmp,Low(cAdcResult)
mov rAdcL,rmp
ldi rmp,High(cOpen)
mov rOpenH,rmp
ldi rmp,Low(cOpen)
mov rOpenL,rmp
ldi rmp,High(cClose)
mov rCloseH,rmp
ldi rmp,Low(cClose)
mov rCloseL,rmp
rcall AdcRdy
DebugLoop:
rjmp DebugLoop
.endif
; Init LED output pins
sbi pLedD,bLedAnRdD ; LED pins as outputs
sbi pLedD,bLEDAnGnD
sbi pLedO,bLedAnRdO ; Set red LED on
cbi pLedO,bLedAnGnO ; Set green LED off
; Init PWM output pin
cbi pPwmO,bPwmO ; Clear PWM output port pin
sbi pPwmD,bPwmD ; Set PWM output pin as output
; Init push button input pins
cbi pBtnDD,bBtnDD ; Push button down as input
sbi pBtnDO,bBtnDO ; Push button down pull-up on
cbi pBtnUD,bBtnUD ; Push button up as input
sbi pBtnUO,bBtnUO ; Push button up pull-up on
; If down button pressed on startup: Set flag for gate down
set ; Upwards by default
sbis pBtnDI,bBtnDI ; Skip if push button down input is set
clt ; Downwards with button down pressed during start-up
; Set flags clear
clr rFlag
; Set start parameters
ldi rmp,High(cPwmOpenDef) ; Slightly upwards on startup
mov rOpenH,rmp
ldi rmp,Low(cPwmOpenDef)
mov rOpenL,rmp
ldi rmp,High(cPwmCloseDef) ; Slightly downwards on startup
mov rCloseH,rmp
ldi rmp,Low(cPwmCloseDef)
mov rCloseL,rmp
ldi rmp,cPwmSpeedDef ; Slowest movement possible
mov rDelta,rmp
; Init TC1 as PWM
ldi rmp,High(cPwm) ; Set the ICR, MSB
out ICR1H,rmp
ldi rmp,Low(cPwm) ; dto., LSB
out ICR1L,rmp
ldi rmp,High(cPwmMid) ; Drive motor to middle position
out OCR1AH,rmp
ldi rmp,Low(cPwmMid)
out OCR1AL,rmp
ldi rmp,(1<<COM1A1)|(1<<WGM11) ; Clear on match A, Fast PWM with ICR
out TCCR1A,rmp
ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10) ; Prescaler=1, Fast PWM with ICR
out TCCR1B,rmp
ldi rmp,1<<OCIE1A ; Enable interrupts on compare match
out TIMSK1,rmp
; Init ADC
clr rAdcL ; Clear sum result
clr rAdcH
ldi rAdcC,64 ; 64 measurements
ldi rmp,(1<<ADC2D)|(1<<ADC1D)|(1<<ADC0D) ; Disable input buffers
out DIDR0,rmp
ldi rmp,0 ; Start with channel 0, ref = operating voltage
out ADMUX,rmp
out ADCSRB,rmp
; Start first conversion
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
out ADCSRA,rmp
; Sleep mode idle, external interrupts INT0 on falling edges
ldi rmp,(1<<SE)|(1<<ISC01)
out MCUCR,rmp
ldi rmp,1<<PCINT7 ; PCINT on pin PA7
out PCMSK0,rmp
ldi rmp,(1<<INT0)|(1<<PCIE0) ; Enable INT0 and PCINT0 interrupts
out GIMSK,rmp
; Enable interrupts
sei ; Enable interrupts
;
; **********************************
; P R O G R A M L O O P
; **********************************
;
Loop:
sleep ; Go to sleep
nop ; Dummy on wakeup
sbrc rFlag,bTC1 ; Skip next if bTC1 flag clear
rcall TC1Ctrl ; Handle TC1 interrupt
sbrc rFlag,bAdc ; Skip if bADC flag clear
rcall AdcRdy ; Handle end of ADC cycle
rjmp loop ; Back to sleep
;
; Handle TC1 interrupt
; Once in each PWM cycle following compare match A
TC1Ctrl:
cbr rFlag,1<<bTC1 ; Clear flag
.if DebugPosition == 1 ; Follow positions up or down
ret
.endif
in ZL,OCR1AL ; Read current position to Z
in ZH,OCR1AH
brts TC1CtrlUp ; Upwards mode is active
; Moving gate downwards
cp ZL,rCloseL ; Check LSB
brne Tc1CtrlDwn1
cp ZH,rCloseH ; Check MSB
breq TC1Post ; LSB+MSB equal, post period
Tc1CtrlDwn1:
; Move gate downwards
sub ZL,rDelta ; Subtract delta
ldi rmp,0
sbc ZH,rmp
cp ZH,rCloseH ; MSB equal?
brcs Tc1CtrlDwn2 ; MSB smaller, set close position
brne Tc1SetPos ; MSB larger, set position
; MSB is equal
cp ZL,rCloseL ; LSB equal?
brcc Tc1SetPos ; LSB larger, set position
Tc1CtrlDwn2:
mov ZL,rCloseL ; Move to close end position
mov ZH,rCloseH
rjmp Tc1SetPos ; Set current position to OCR1A
TC1CtrlUp:
; Move gate upwards
cp ZL,rOpenL ; Compare LSB
brne TC1CtrlUp1
cp ZH,rOpenH ; Compare MSB
breq TC1Post ; Reached end position
TC1CtrlUp1:
; rClose 1500 .. 800
; rOpen 1500 .. 2200
add ZL,rDelta ; Add rDelta
ldi rmp,0
adc ZH,rmp
cp ZH,rOpenH ; Compare MSB
brcs Tc1SetPos ; MSB smaller, set position
brne Tc1CtrlUp2 ; MSB larger, set open position
; MSB equal
cp ZL,rOpenL ; Compare LSB
brcs Tc1SetPos ; LSB smaller, set position
TC1CtrlUp2:
mov ZL,rOpenL ; Set open end position
mov ZH,rOpenH
; Write the position in Z to OCR1A
TC1SetPos:
out OCR1AH,ZH
out OCR1AL,ZL
ldi rmp,cPost ; Start value post period
mov rPost,rmp ; to rPost
in rmp,TCCR1A ; Check if COM1A1 is on
andi rmp,1<<COM1A1 ; Check COM1A1
brne TC1Blink ; Is active
ldi rmp,(1<<COM1A1)|(1<<WGM11) ; Switch COM1A1 on
out TCCR1A,rmp ; Set pin active
TC1Blink:
; Gate is moving, blink the red LED
sbi pLedO,bLedAnRdO ; Red LED on
inc rBlink
mov rmp,rBlink
sbrc rmp,2 ; Blink with bit 2
cbi pLedO,bLedAnRdO ; Red LED off
cbi pLedO,bLedAnGnO ; Green LED off
ret
; Gate reached end position, post period
TC1Post:
tst rPost ; Post period over?
breq TC1Ret ; Yes, done
dec rPost ; Count post period down
brne TC1Blink ; Not yet over, blink red
; Post period over, disable OC1A pin
ldi rmp,1<<WGM11 ; Only the WGM bits
out TCCR1A,rmp ; Disable pin
cbi pPwmO,bPwmO ; clear PWM pin
brts TC1UpLed
; Gate in closed position, LED is red
sbi pLedO,bLedAnRdO ; Red LED on
cbi pLedO,bLedAnGnO ; Green LED off
.if DebugOpenClose == 1
set
.endif
ret
TC1UpLed:
cbi pLedO,bLedAnRdO ; Red LED off
sbi pLedO,bLedAnGnO ; Green LED on
.if DebugOpenClose == 1
clt
.endif
TC1Ret:
ret
;
; ADC has ended cycle for channel
AdcRdy:
cbr rFlag,1<<bAdc ; Clear ADC flag
in rmp,ADMUX ; Read channel
cpi rmp,1 ; Channel 1?
breq AdcRdy1 ; Yes
brcc AdcRdy2 ; Channel 2
; Current channel = 0
rcall MultiplyPos
sub ZL,R0 ; Sub result from mid
sbc ZH,R1
mov rCloseL,ZL
mov rCloseH,ZH
.if DebugClose == 1
out OCR1AH,ZH
out OCR1AL,ZL
.endif
ldi rmp,1 ; Continue with channel 1
rjmp AdcRst
AdcRdy1:
; Current channel = 1
rcall MultiplyPos
add ZL,R0 ; Add result to mid
adc ZH,R1
mov rOpenL,ZL
mov rOpenH,ZH
.if DebugOpen == 1
out OCR1AH,ZH
out OCR1AL,ZL
.endif
ldi rmp,2
rjmp AdcRst
AdcRdy2:
; Invert MSB ADC sum
com rAdcH
; Current channel = 2
; Convert MSB ADC to
; 50..350 (1 to 7 seconds time,
; 50..350 Compare changes
; Registers: R2:R1:R0 = 300
; R5:R4:R3 = Result
; Divide (rClose-rOpen) by result
; to yield result per step
; Registers: R2:R1 as divider
; R5:R4:R3 as divident
; rmp as result
; Examples:
; rClose = 2000, rOpen = 1000
; MSB ADC = 0xFF:
; Multiplication result = 50
; Division result = 1000 / 50 = 20
; MSB ADC = 0x00
; Multiplication result = 350
; Division result = 1000 / 350 = 2
; Load multiplicator (default 300)
ldi rmp,LOW(cSpdMul)
mov R0,rmp
ldi rmp,HIGH(cSpdMul)
mov R1,rmp
clr R2
; Load result with adder
clr R3
ldi rmp,Low(cSpdAdd)
mov R4,rmp
ldi rmp,High(cSpdAdd)
mov R5,rmp
; Multiply rAdcH with R1:R0
; result in R5:R4:R3
AdcRdy2a:
lsr rAdcH ; Shift lowest bit to carry
brcc AdcRdy2b ; 0, do not add
add R3,R0 ; Add the current shift to result
adc R4,R1
adc R5,R2
AdcRdy2b:
lsl R0 ; Shift multiplicator one left
rol R1
rol R2
tst rAdcH ; Check multiplication done?
brne AdcRdy2a ; No
lsl R3 ; LSB of result for rounding
brcc AdcRdy2c
inc R4 ; Round LSB up
brne AdcRdy2c
inc R5 ; Round MSB up
; Multiplication result is in R5:R4
AdcRdy2c:
; calculate difference between max and min
; in R2:R1:R0
mov R1,rOpenH
mov R0,rOpenL
sub R0,rCloseL
sbc R1,rCloseH
clr R2
; Divide difference in R1:R0 by R5:R4
; rmp is 8 bit result
ldi rmp,1
AdcRdy2d:
lsl R0 ; Shift divident left
rol R1
rol R2
brcs AdcRdy2e ; If carry, shift a one into result
cp R1,R4 ; Compare with divisor
cpc R2,R5
brcc AdcRdy2e ; Shift 1 into result
clc ; Shift 0 into result
rjmp AdcRdy2f
AdcRdy2e:
sub R1,R4 ; Subract divisor
sbc R2,R5
sec ; Shift one into the result
AdcRdy2f:
rol rmp
brcc AdcRdy2d
lsl R0 ; Shift divident left for rounding
rol R1
rol R2
brcs AdcRdy2g ; If a one rolls out: round up
cp R1,R4 ; Compare LSB
cpc R2,R5 ; and MSB
brcs AdcRdy2h ; Smaller, do not round up
AdcRdy2g:
inc rmp ; Round up
AdcRdy2h:
tst rmp ; Check if result is zero
brne AdcRdy2i
inc rmp ; Add one to zero in rmp
AdcRdy2i:
mov rDelta,rmp ; Copy result to rDelta
ldi rmp,0 ; Restart with channel 0
AdcRst:
out ADMUX,rmp
clr rAdcL
clr rAdcH
ldi rAdcC,64
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
out ADCSRA,rmp
ret
;
; Multiplication of MSB ADC result
MultiplyPos:
ldi rmp,Low(cPwmDelta) ; default: 500, to R2:R1:R0
mov R0,rmp
ldi rmp,High(cPwmDelta)
mov R1,rmp
clr R2
clr R3 ; Clear result
clr R4
clr R5
MultiplyPos1:
lsr rAdcH ; Shift bit to carry
brcc MultiplyPos2 ; Not one
add R3,R0
adc R4,R1
adc R5,R2
MultiplyPos2:
lsl R0 ; Multiply by two
rol R1
rol R2
tst rAdcH ; Check all multiplied
brne MultiplyPos1
lsl R3 ; Round up?
brcc MultiplyPos3
ldi rmp,0 ; Round one up
adc R4,rmp
adc R5,rmp
MultiplyPos3:
mov R0,R4 ; Copy result to R1:R0
mov R1,R5
ldi ZH,High(cPwmMid) ; Load mid to Z
ldi ZL,Low(cPwmMid)
ret
;
; End of source code
Top of page |
---|