![]() |
AVR applications Intensity and battery control with an ATtiny13 Assembler source code for the spotlight |
;
; *********************************
; * 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