Path:Home => AVR-EN => Applications => Spotlight ATtiny13 => Assembler software
Spotlight small AVR applications

Intensity and battery control with an ATtiny13
Assembler source code for the spotlight
Logo

Assembler source code for the ATtiny13 spotlight

The original source code in asm format is here.

;
; *********************************
; * Spotlight with ATtiny13       *
; * (C)2018 avr-asm-tutorial.net  *
; *********************************
;
.nolist
.include "tn13adef.inc" ; Define device ATtiny13A
.list
;
; **********************************
;  D E B U G G I N G   S W I T C H
; **********************************
;
; This debugs the voltage LED control
.equ debug_vtg = 0 ; 1=Debugging voltage on
  .if debug_vtg == 1 ; Setting debugging parameters
    .equ debug_voltage = 30000 ; Voltage in mV
    .endif
;
; This sets 15 Volt operating voltage
.equ debug_15V = 0 ; 1=Debugging 15 V operating voltage
  .if debug_15V == 1 ; Setting debugging parameteres
    .equ cVtgGnHigh =18000 ; Highest voltage in mV, LED is green
    .equ cVtgNom = 15000 ; Normal voltage in mV, LED is green
    .equ cVtgGnMin = 13000 ; Minimum voltage in mV, LED is green
    .equ cVtgRdMin = 11000 ; Voltage in mV where LEDs is fully red
    .endif
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATtiny13A, Package: 8-pin-PDIP_SOIC
;            ___________
;         1 /           |8
; RESET o--|RES      VCC|--o +5V
;         2|            |7
;  Poti o--|PB3      PB2|--o LED rd anode
;         3|            |6
; Voltg o--|PB4      PB1|--o LED gn anode & PWM
;         4|            |5
;   0 V o--|GND      PB0|--o PWM constant-current-driver
;          |____________|
;

; **********************************
;  P O R T S   A N D   P I N S
; **********************************
;
.equ pOutO = PORTB ; Define the output port
.equ pDirD = DDRB ; Define the direction port
.equ bPwmO = PORTB0 ; Define the PWM output pin
.equ bGnAO = PORTB1 ; Define the green LED anode output pin
.equ bRdAO = PORTB2 ; Define the red LED anode output pin
.equ bAnyD = (1<<DDB0)|(1<<DDB1)|(1<<DDB2) ; Direct pins output
;
; **********************************
;   A D J U S T A B L E   C O N S T
; **********************************
;
.equ clock=1200000 ; Define clock frequency
;
; Voltage measurement constants
.equ cR1 = 10 ; Lower resistor in kOhm
.equ cR2 = 82 ; Upper resistor in kOhm
.equ cVtgMax=(5000*(cR1+cR2)+cR1/2)/cR1 ; Maximum measurable voltage in mV
.if debug_15V == 0 ; Not debugging, normal operation
  .equ cVtgGnHigh =37000 ; Highest voltage in mV, LED is green
  .equ cVtgNom = 35000 ; Normal voltage in mV, LED is green
  .equ cVtgGnMin = 29600 ; Minimum voltage in mV, LED is green
  .equ cVtgRdMin = 26000 ; Voltage in mV where all the LEDs turn off
  .endif
; Set minimum voltage LED cycles
;   Led is green: at cVtgGnMin 25 out of 256 PWM cycles on
;   Led is red: at cVtgRdMax 25 out of 256 PWM cycles on
.equ cLedMin = 25 ; Minimum brightness of LED
;
; Multiplicator setting for green and red LED PWM
.equ cFactorGn = 256 ; Try with a multiplicator of 256 on green
.equ cFactorRd = 256 ; Try with a multiplicator of 256 on red
;
; Check multiplication factors
.if (cFactorGn!=256) && (cFactorGn!=512)
  .error "cFactorGn can only be 256 or 512!"
  .endif
.if (cFactorRd!=256) && (cFactorRd!=512)
  .error "cFactorRd can only be 256 or 512!"
  .endif
;
; **********************************
;  F I X  &  D E R I V.  C O N S T
; **********************************
;
; Timer TC0
.equ cTc0Presc = 64 ; Prescaler for TC0
.equ cPwmFrq = clock / cTc0Presc / 256 ; PWM frequency
;
; Voltage calculation
.equ cAdcCnt = 64 ; Number of measurement counts
.equ cAdcMult =(1023*cR1*cAdcCnt+(cR1+cR2)/2)/(cR1+cR2) ; Multiplicator mV to Adc
.equ cAdcGnHigh=(cVtgGnHigh*cAdcMult+2500)/5000 ; Maximum Green Adc sum
.equ cAdcGnMin=(cVtgGnMin*cAdcMult+2500)/5000 ; Minimum Green Adc sum
.equ cAdcRdHigh=cAdcGnMin-1 ; LED turns red equal or below this Adc sum
.equ cAdcRdMin=(cVtgRdMin*cAdcMult+2500)/5000 ; Adc sum when red LED is fully on
.equ cAdcMulGn=(cFactorGn*(255-cLedMin)+(cAdcGnHigh-cAdcGnMin)/2)/(cAdcGnHigh-cAdcGnMin) ; Multiplicator for green
.equ cAdcMulRd=(cFactorRd*(255-cLedMin)+(cAdcRdHigh-cAdcRdMin)/2)/(cAdcRdHigh-cAdcRdMin) ; Multiplicator red
;
; Check the range of the multiplication factors
.set MultiFactor = 0 ; No error
.if cAdcMulGn>=256 ; Green factor out of range
  .error "The green multiplication factor is out of range!"
  .set MultiFactor = 1 ; Set error condition
  .else
  .if cAdcMulGn>=256 ; Red factor out of range
    .error "The red multiplication factor is out of range!"
    .set MultiFactor = 2 ; Set error condition
    .endif
  .endif
.if MultiFactor == 0 ; No error
  ; Check the rounding effect of the multiplicators
  .if cFactorGn==256 ; Check if cFactorGn at 256
    .equ cAdcMulGnTst=(512*(255-cLedMin)+(cAdcGnHigh-cAdcGnMin)/2)/(cAdcGnHigh-cAdcGnMin) ; Multiplicator for green
    .if cAdcMulGnTst<256 ; Check range of factor 512
      .if (cAdcMulGnTst!=(2*cAdcMulGn)) ; Check if 512 is more accurat
        .message "Green LED: Using 512 for cFactorGn would be better!"
        .endif
      .endif
    .endif
  .if cFactorRd==256 ; Check if cFactorGn at 256
    .equ cAdcMulRdTst=(512*(255-cLedMin)+(cAdcRdHigh-cAdcRdMin)/2)/(cAdcRdHigh-cAdcRdMin) ; Multiplicator red
    .if cAdcMulRdTst<256
      .if cAdcMulRdTst!=(2*cAdcMulRd) ; Check if 512 more accurat
        .message "Red LED: Using 512 for cFactorRd would be better!"
        .endif
      .endif
    .endif
  .endif
;
; Simulate debugging of voltage display solely
  .if debug_vtg == 1 ; Debugging parameters
    .equ debug_adcvtg=(debug_voltage*cR1+(cR1+cR2)/2)/(cR1+cR2) ; Voltage in mV
    .if debug_adcvtg>5000
      .error "Voltage on ADC input is too high! See smbol debug_adcvtg!"
      .endif
    .equ debug_adcsum=(debug_voltage*cAdcMult+2500)/5000 ; Convert to ADC sum
    .endif
;
; **********************************
;          T I M I N G
; **********************************
;
; TC0 is the 8-bit PWM
;   clock               = 2,400,000 Hz
;   TC0 prescaler       =         8
;   TC0 clock frequency =   300,000 Hz
;   PWM clock cycles    =       256
;   PWM cycle frequency =     1,171.9 Hz
;   PWM pulse duration  =       853.3 us
;
; ADC measurements of the voltages on
;   a) the potentiometer voltage (intensity)
;   b) the voltage input pin
;   are initiated by the TC0 compare match,
;   at the end of the on period of the LEDs.
;   On each channel a) and b) eight measurements
;   are summed up and the Compare A and B values
;   are calculated and set.
;   OCR0A matches          = 1,171.9 Hz
;   Number of channels     =     2
;   Measurements summed up =    64
;   Update frequency       =     9.155 Hz
;   Update cycle           =   109.2 ms
;
; Duration of ADC measurements
;   TC0 compare matches trigger AD conversion
;     therefore conversion time has to be
;     shorter than trigger duration
;   clock                     = 2,400,000 Hz
;   ADC clock prescaler       =        16
;   ADC clocks per conversion =        13
;   Conversion duration       =        86.7 us
;   % of PWM cycle time       =        10.2%
;
; **********************************
;       R E G I S T E R S
; **********************************
;
; free: R0 to R5
.def rMul0  = R6 ; Multiplication result, Byte 0
.def rMul1  = R7 ; dto., Byte 1
.def rMul2  = R8 ; dto., Byte 2
.def rAdc0  = R9 ; Multiplicator, Byte 0
.def rAdc1  = R10 ; dto., Byte 1
.def rAdc2  = R11 ; dto., Byte 2
.def rAdcRL = R12 ; Last ADC result, LSB
.def rAdcRH = R13 ; dto., MSB
.def rAdcL  = R14 ; ADC result sum, LSB
.def rAdcH  = R15 ; dto., MSB
.def rmp    = R16 ; Multipurpose register
.def rAdc   = R17 ; ADC counter
; free: R18 to R29
; used: R31:R30 = Z for diverse purposes
;
; **********************************
;           S R A M
; **********************************
;
; ( No SRAM used)
;
; **********************************
;         C O D E
; **********************************
;
.cseg
.org 000000
;
; **********************************
; R E S E T  &  I N T - V E C T O R S
; **********************************
  rjmp Main ; Reset vector
  reti ; INT0, not used
  reti ; PCI0, not used
  reti ; OVF0, not used
  reti ; ERDY, not used
  reti ; ACI, not used
  reti ; OC0A, not used
  reti ; OC0B, not used
  reti ; WDT, not used
  rjmp AdcIsr ; ADCC
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; ADC conversion complete interrupt service routine
AdcIsr:
  set ; Set T flag
  reti
;
; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************
;
Main:
  ldi rmp,Low(RAMEND)
  out SPL,rmp ; Init LSB stack pointer
  ; Increase clock frequency to 2.4 MHz
  ldi rmp,1<<CLKPCE ; Enable clock frequency change
  out CLKPR,rmp
  ldi rmp,1<<CLKPS1 ; Divide 9.6 MHz RC clock by 4
  out CLKPR,rmp
  ; Init ports
  ldi rmp,bAnyD ; Direction output for digital pins
  out pDirD,rmp ; to direction port
  ldi rmp,0 ; All outputs low
  out pOutO,rmp ; in port register
  ; Init ADC
  ldi rmp,(1<<ADC0D)|(1<<ADC2D)|(1<<ADC3D) ; Disable digital input pins
  out DIDR0,rmp ; in disable port
  ldi rmp,(1<<MUX1) ; Start with potentiometer channel
  out ADMUX,rmp ; to ADC multiplexer
  clr rAdcL ; Clear sum, LSB
  clr rAdcH ; dto., MSB
  ldi rAdc,cAdcCnt ; Start 8 measurements
  ldi rmp,(1<<ADTS1)|(1<<ADTS0) ; Start ADC on TC0 Compare Match A
  out ADCSRB,rmp ; in ADC control port B
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2) ; Presc=16, ADATE, Int
  out ADCSRA,rmp ; in ADC control port
  ; Init TC0 as PWM and ADC starter
  ldi rmp,0 ; Set PWM outputs off
  out OCR0A,rmp ; in compare A, light array
  out OCR0B,rmp ; and in compare B, voltage control LED
  ldi rmp,(1<<COM0A1)|(1<<COM0B1)|(1<<WGM01)|(1<<WGM00) ; Fast PWM, clear on match
  out TCCR0A,rmp ; to TC0 control port A
  ldi rmp,1<<CS01 ; Prescaler = 8
  out TCCR0B,rmp ; to TC0 control port B
  ; Debug voltage
  .if debug_vtg == 1 ; Debugging parameters
    ldi rmp,Low(debug_adcsum) ; Set ADC sum, LSB
    mov rAdcRL,rmp
    ldi rmp,High(debug_adcsum) ; dto., MSB
    mov rAdcRH,rmp
    rjmp AdcDbg
    .endif
  ; Sleep mode enable
  ldi rmp,1<<SE ; Sleep enable, sleep mode idle
  out MCUCR,rmp
  sei ; Enable interrupts for ADC
;
; **********************************
;    P R O G R A M   L O O P
; **********************************
;
Loop:
  sleep ; Go to sleep
  nop ; Dummy for wake-up
  brtc Loop ; If T flag not set
  clt ; Clear flag
  ; Read the ADC result
  in rmp,ADCL ; Read result, LSB
  add rAdcL,rmp ; Add LSB
  in rmp,ADCH ; dto., MSB
  adc rAdcH,rmp ; dto., MSB
  dec rAdc ; Count measurements
  brne Loop ; Back to loop
  ldi rAdc,cAdcCnt ; Restart 64 measurements
  mov rAdcRL,rAdcL ; Copy LSB result
  mov rAdcRH,rAdcH ; Copy MSB result
  clr rAdcL ; Restart sum, LSB
  clr rAdcH ; dto., MSB
  sbic ADMUX,MUX0 ; Read current channel
  rjmp AdcVtg ; Voltage has been measured
  ; Potentiometer has been measured 64 times
  ; MSB result is PWM value for LEDs
  ldi rmp,(1<<MUX1)|(1<<MUX0) ; Channel ADC3
  out ADMUX,rmp ; to ADMUX
  out OCR0A,rAdcRH ; Write MSB result to compare A
  rjmp Loop ; Back to loop
AdcVtg:
  ; Voltage has been measured
  ; Set channel MUX to ADC2
  ldi rmp,(1<<MUX1) ; Channel ADC2
  out ADMUX,rmp ; to ADMUX
AdcDbg:
  mov rAdc0,rAdcRL ; Copy measuring result, LSB
  mov rAdc1,rAdcRH ; dto., MSB
  ldi rmp,Low(cAdcGnHigh) ; Load max. ADC sum
  cp rAdc0,rmp ; Compare with maximum green, LSB
  ldi rmp,High(cAdcGnHigh)
  cpc rAdc1,rmp ; dto., MSB
  ldi rmp,255 ; Fully green
  brcc AdcSetPwmGn ; Set PWM green
  ldi rmp,Low(cAdcGnMin) ; Subtract green minimum, LSB
  sub rAdc0,rmp
  ldi rmp,High(cAdcGnMin) ; dto., MSB
  sbc rAdc1,rmp
  brcs AdcLow ; Voltage smaller than min.
  ldi rmp,cAdcMulGn ; Load multiplicator
  rcall Multiply ; Multiply the difference
  .if cFactorGn == 512
    lsr rMul2 ; Divide by 2
    ror rMul1
    ror rMul0
    .endif
  ldi rmp,cLedMin ; Minimum pulse width
  add rmp,rMul1 ; Add to MSB result
AdcSetPwmGn:
  out OCR0B,rmp ; Write result to OCR-B
  cbi pOutO,bRdAO ; Clear red anode
  rjmp Loop
AdcLow:
  mov rAdc0,rAdcRL ; Copy measuring result, LSB
  mov rAdc1,rAdcRH ; dto., MSB
  ldi rmp,Low(cAdcRdMin) ; Subtract red minimum, LSB
  sub rAdc0,rmp
  ldi rmp,High(cAdcRdMin) ; dto., MSB
  sbc rAdc1,rmp
  ldi rmp,0x00
  brcs AdcSet ; Voltage smaller than min, set fully red
  ldi rmp,cAdcMulRd ; Set red multiplicator
  rcall Multiply
  .if cFactorRd == 512
    lsr rMul2 ; Divide by 2
    ror rMul1
    ror rMul0
    .endif
  mov rmp,rMul1 ; Set multiplicator result
AdcSet:
  out OCR0B,rmp ; Write rmp to compare B
  sbi pOutO,bRdAO ; Set red anode
  rjmp loop
;
; Multiply rAdc1:rAdc0 by rmp
;   Result is in rMul2:rMul1:rMul0
;   Result is in rMul1
Multiply:
  clr rAdc2 ; Clear upper byte
  clr rMul0 ; Clear result, byte 0
  clr rMul1 ; dto., byte 1
  clr rMul2 ; dto., byte 2
Multiply1:
  lsr rmp ; Shift multiplicator right
  brcc Multiply2 ; Not one, skip adding
  add rMul0,rAdc0 ; Add multiplicator, byte 0
  adc rMul1,rAdc1 ; dto., byte 1
  adc rMul2,rAdc2 ; dto., byte 2
Multiply2:
  lsl rAdc0 ; Shift multiplicator left
  rol rAdc1 ; dto., byte 1
  rol rAdc2 ; dto., byte 2
  tst rmp ; Multiplicator = 0?
  brne Multiply1
  ldi rmp,0x80
  add rMul0,rmp
  ldi rmp,cLedMin ; Add minimum
  adc rMul1,rmp
  ldi rmp,0
  adc rMul2,rmp
  tst rMul2
  breq Multiply3
  ldi rmp,0xFF
  mov rMul1,rmp
Multiply3:
  ret
;
; End of source code



Praise, error reports, scolding and spam please via the comment page to me.

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