Path: AVR_EN => Applications => Model railway crossing gate => Source code version 2
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, version 2

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

;
; ***********************************
; * 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


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