Full Version : ATtiny13 LED Solar Tracker (AVR ASM)
avr >>VISION GUIDANCE & NAVIGATION >>ATtiny13 LED Solar Tracker (AVR ASM)


AVR_Admin- 05-14-2006
Tiny13 Led Solar Tracker @ 9.6 Mhz by John-Alfred Ullasmann


As a further development of the Opto Circuit, I thought I would bring you up to speed with the current state of my experimental research.

Suppose it were possible to design and build a Solar Tracker Circuit for less than $2 , who wouldn’t be interested in hearing more about this ?

What if this circuit could output the current position of the sun in Degrees with an accuracy of 1 part in 256 using an 8 Bit and 1 part in 1024 using a 10 Bit A2D ?

If, in addition, the Circuit could output the total incident sunlight falling on the 4 compass Headings, would that not make this circuit almost irresistible ?

All the best from sunny Germany.

John-Alfred Ullasmann

CODE

;  Tiny13 Led Solar Tracker @ 9.6 Mhz

;-----        
.include  "tn13def.inc"


;------------------------------------------------------------------------------------------------
.def  RemLo   = r6 ; 16 Bit Division Remainder
.def  RemHi   = r7

.def  LiteLo   = r8 ; 16 Bit Light Level
.def  LiteHi   = r9

.def  StorLo   = r10 ; 16 Bit Division Temporary Store Register
.def  StorHi   = r11

.def ValLo  = r12 ; 16 Bit A2D Average
.def ValHi  = r13

.def BytOut  = r14 ; 8 Bit Serial Out Register
.def LupCnt  = r15 ; 256 A2D Sample Counter

.def Temp  = r16 ; Work Register A
.def Work  = r17 ; Work Register B

.def BitCnt  = r18 ; Serial Output Bit Counter
.def SrtCnt  = r18 ; Sort Routine Loop Count

.def BitDly  = r19 ; 104 uS Delay Constant
.def  DivCnt   = r19 ; 16 Bit Division Routine Bit Count

.def  ConCnt   = r20 ; A2D Loop Count
.def  DisPin   = r21 ; Discharge Pin

.def  LuxLo   = r20 ; 2 Highest Light 8 Bit Values
.def  LuxHi   = r21

.def  DivrLo   = r20 ; 16 Bit Divider
.def  DivrHi   = r21

.def  WordLo   = r20 ; 16 Bit Serial Out Word
.def  WordHi   = r21

.def  DirLo   = r20 ; 2 Highest Direction 8 Bit Values
.def  DirHi   = r21

.def  DivdLo  = r22 ; 16 Bit Dividend
.def  DivdHi   = r23

.def  HoldB  = r22 ; Sort Temporary Hold Registers
.def  HoldA   = r23

.def  ResLo   = r22 ; 16 Bit Result
.def  ResHi   = r23

.def  MulLo  = r22 ; 16 Bit Multiplicand
.def  MulHi   = r23

.def  DirInd  = r22 ; Compass Direction Select Register
.def  AdcSel   = r23 ; A2D Multiplexor Select Register


;------------------------------------------------------------------------------------------------        
.Cseg
       
.Org $0000


;------------------------------------------------------------------------------------------------      
Reset:            
      ldi Temp, RamEnd  
     out Spl, Temp ; Set Stack Pointer

sbi AcsR, Acd ; Disable Comparator

Ldi Temp, $3c ; Disable all AdcD Digital Inputs  
out DidR0, Temp      
       
ldi Temp, $87 ; Init A2D + 75 Khz Sample Rate
out AdCsrA, Temp

ldi YH, $00 ; Set YH Constant to Zero

clr LupCnt  ; 256 Sample Counter


;------------------------------------------------------------------------------------------------        
Main:
ldi YL, LuxBuf+$00; Point YL to Light Value Start
 
ldi AdcSel, $60 ; Adc0 + ( AdLar = 1 ) + 1.1V Ref

ldi DirInd, $00 ; Direction Identifier

ldi DisPin, $01  ; Mark on PB0 + Discharge Led

clr LiteHi  ; Clear 16-Bit  Light Level  
clr LiteLo

ldi ConCnt, 4 ; Conversion Loop Count = 4 Leds

rcall Convert  ; Do A2D Conversion
dec ConCnt
brne pc-2  ; 4 * 256 samples finished ?

rcall Sort  ; Bubble Sort Results in Ascending order

rcall Intensity ; Get the 2 Brightest Light Values

rcall Mul90  ; Multiply Result by Ninety

rcall Divide  ; 16 Bit Division

rcall Compass  ; Get Compass Quadrant in Degrees

rcall Send  ; Transmit Data Stream

rjmp Main  ; Go back


;------------------------------------------------------------------------------------------------
Convert:

;* This routine gets 256* A2D, 8-Bit samples on each Led. These samples are then averaged and
;* integrated in "ValHi:ValLo" and the final result is divided by 256 and is stored in "Valhi"

;* This value is then placed in the Ram locations
;* starting @ "LuxBuf+$00" using Y register Post Indexing.

;* The Compass Point Direction "DirInd" 8-Bit values (N,E,S,W) are similarly stored
;* in the Ram locations starting @ "LuxBuf+$00" using Y register offset by + $04

;* The total light incidence falling on the 4 Leds is likewise averaged over 256 samples and is
;* stored in Registers "LiteHi:LiteLo"

;* "AdcSel" is incremented after each pass and is used to select the appropriate A2D Mux Input
;* (00,01,02,03)

;* "DisPin" is  also incremented after each pass and is used to "discharge" the appropriate
;* Led by bringing the corresponding Mux Input Low


out AdMux, AdcSel ; AdcN + (AdLar=1) + Int Voltage Ref
   
clr ValLo  ; Clr Result Registers
clr ValHi

clr LiteLo  ; Clr Accumulated Light Registers
clr LiteHi

ConLup:
sbi AdCsrA, Adsc  ; En A/D Conv
       
      sbis AdCsrA, Adif ; Conv Comp ?  
rjmp pc-1

sbi AdCsrA, Adif ; Clr Conversion Flag

      in Temp, AdcH ; Read Hi order 8 Bits

adc ValLo, Temp ; Integrate
adc ValHi, YH

dec LupCnt  ; 256 samples done ?
brne ConLup

st Y+, ValHi ; Store Value in LuxBuf+$nn + Increment Y

std Y+4, DirInd ; Put Direction in LuxBuf+$nn offset by 4

add LiteLo, ValHi ; Update accumulated Light Level
adc LiteHi, YH ; Adc Null Constant

out DdrB, DisPin ; Discharge Direction Led

ldi Temp, $01 ; Disharge Led Voltage
out PortB, Temp

out DdrB, Temp ; ReEnable Mark on PB0
out PortB, Temp

inc AdcSel  ; Select Next Led

inc DisPin  ; Select Next Discharge Pin

inc DirInd  ; Select Next Direction Indicator

ret


;------------------------------------------------------------------------------------------------
Sort:    

;* This routine Bubble Sorts the Four 8-Bit Light Values starting @ Ram Location "LuxBuf+0".
;* The corresponding Direction Indicators starting @ Ram Location "LuxBuf+4" are also sorted.

ldi SrtCnt, $04 ; Sort Loop Counter = 4

OutLup:
ldi YL, LuxBuf+3; Point to End of Light values
 
 ld  HoldA, Y  ; Get First Value in HoldA
   
ld  HoldB, -Y  ; Get 2nd Value in HoldB
   
cp  HoldA, HoldB  ; Compare both values and ...
brlo  ChkLup      

st  Y, HoldA ; Swap A and B if A>B  
std  Y+1, HoldB

ldd HoldA, Y+5 ; Do same with Direction Indicators
ldd HoldB , Y+4

std Y+4, HoldA ; Swap Indicators  
std Y+5, HoldB

ChkLup:  
cpi YL, LuxBuf+0; Loop Inner if not finished
brne  OutLup+1

dec  SrtCnt  ; Loop to back OutLup if NOT finished
brne  OutLup

ret


;------------------------------------------------------------------------------------------------
Intensity:

;* This routine fetches the 2 Brightest Light Intensities and prepares
;* the result in Degrees according to the Formula
;*            
;* ((Highest Lux Value / (Highest Lux Value + 2nd Highest Value)) * 90) + Compass point


ld LuxHi, Y+ ; Highest + 2nd Highest Light Values
ld LuxLo, Y

mov MulLo, LuxHi ; Copy LuxHi into MulLo

add LuxLo, LuxHi ; Add Highest to 2nd Highest Light Value
clr LuxHi
adc LuxHi, YH ; Adc Zero Constant

ret

 
;------------------------------------------------------------------------------------------------
Mul90:

;* This routine multiplies the 8-bit number "MulLo" by 90
;* using just Shifts and Adds. The 16-bit result is placed in "MulHi:MulLo".


clr StorLo  ; Temp Holding Store
clr StorHi

clr MulHi  ; Clear High Byte Result

lsl MulLo  ; Multiplicand * 2
rol MulHi
add StorLo, MulLo ; Add to Holding
adc StorHi, MulHi

lsl MulLo  ; Multiplicand * 8
rol MulHi
lsl MulLo
rol MulHi
add StorLo, MulLo ; Add to Holding
adc StorHi, MulHi

lsl MulLo  ; Multiplicand * 16
rol MulHi
add StorLo, MulLo ; Add to Holding
adc StorHi, MulHi

lsl MulLo  ; Multiplicand * 64
rol MulHi
lsl MulLo
rol MulHi
add MulLo, StorLo ; Result = Holding + Multiplicand
adc MulHi, StorHi

ret


;------------------------------------------------------------------------------------------------
Divide:  

;* This routine divides the two 16-bit numbers
;* "DivdHi:DivdLo" (Dividend) and "DivrHi:DivrLo" (Divisor).
;* The result is placed in "ResHi:ResLo" and the Remainder in "RemHi:RemLo".

clr RemHi  ; Clear Remainder Registers
clr RemLo    

ldi DivCnt,16 ; Init Loop Counter
 
lsl DivdLo  ; Shift Left Dividend
rol DivdHi
rol RemLo  ; Rotate Dividend into Remainder
rol RemHi
 
cp RemLo, DivrLo ; Remainder ? Divisor
cpc RemHi, DivrHi
brcs pc+4  ; If Result Negative then Skip
 
inc DivdLo  ; Increment Dividend Low
sub RemLo, DivrLo  
sbc RemHi, DivrHi ; Remainder = Remainder - Divisor
 
dec DivCnt  ; If not done then Iterate
brne pc-11

ret


;------------------------------------------------------------------------------------------------
Compass:

;* This routine gets the Compass Quadrant as a 16-Bit Degree Value
;* This Value is then added to the accumulated Sum in ResHi:ResLo

lds Work, LuxBuf+$04; Get Brightest Direction

lds Temp, LuxBuf+$05; Get Second Brightest Direction

cpi Work, $00 ; = North ?
breq North

cpi Work, $01 ; = East ?
breq East

cpi Work, $02 ; = South ?
breq South

West:
cpi Temp, $02 ; = South ?
breq SWest

NWest:
ldi Temp, $0e ; = 270 Degrees
ldi Work, $01
rjmp Exit

SWest:
ldi Temp, $b4 ; = 180 Degrees
rjmp Bye

SEast:
ldi Temp, $5a ; = 90 Degrees
rjmp Bye

NEast:
clr Temp  ; = 0 Degrees

Bye:
clr Work  ; = 0 Always

Exit:
add ResLo, Temp ; Add Compass Point to accumulated Sum
adc ResHi, Work
ret   ; Return

North:
cpi Temp, $03 ; = West ?
breq NWest
rjmp NEast

East:
cpi Temp, $02 ; = South ?
breq SEast
rjmp NEast

South:
cpi Temp, $01 ; = East ?
breq SEast
rjmp SWest


;------------------------------------------------------------------------------------------------
Send:      

;* This Software Uart routine running @ 9600 Bps sends 3* 16-Bit values to the Host.
;* The data stream is transmitted Byte wise using (1 Start,8 Data,1 Stop Bits)

;* The Info Packet consists of the Sun's orientation in Degrees "ResHi:ResLo"
;* The Modulo Remainder of the division routine in "RemHi:RemLo"
;* The total incident sunlight in "LiteHi:LiteLo"

;* If so desired the Delay Constant value "BitDly" can be adjusted to allow other
;* data transmission speeds.


movw WordHi:WordLo,ResHi:ResLo; 16 Bit Degree Result  
rcall PrWord    

movw WordHi:WordLo,RemHi:RemLo; 16 Bit Remainder
rcall PrWord

movw WordHi:WordLo,LiteHi:LiteLo; 16 Bit Light Value

;------
PrWord:      
mov BytOut, WordHi
rcall PrByt  ; Send High 8 Byte

mov BytOut, WordLo ; Send Low  8 Byte

;------
PrByt:      
mov Temp, BytOut ; Fetch BytOut

swap Temp
rcall NibAsc  ; Nibble to Ascii

mov Temp, BytOut ; Fetch BytOut once more

;------
NibAsc:      
andi Temp, $0f ; Mask High Nibble

cpi Temp, $0a ; > 10 ?
brcs pc+2

subi Temp, -$07 ; If No then Add 48+7 to Temp

subi Temp, -$30 ; If No then Add only 48 to Temp

;------
PrChr:
ldi BitCnt, 9

clc   ; Start Bit

rcall BiTime  ; Out Bit

lsr Temp  ; Shift next bit out
dec Bitcnt    
brne pc-3  ; Loop if Not Zero

sec   ; Stop Bit

;-----
BiTime:
brcs pc+3  ; Goto Mark

cbi PortB,  PortB0 ; Space Bit on PB0
rjmp Delay

sbi PortB,  PortB0 ; Mark Bit on PB0
nop

;-----
Delay:  
ldi   BitDly, $f8 ; Delay 104 uS

nop
  dec BitDly  ; Decrement Count and Loop
       brne  pc-2
ret


;------------------------------------------------------------------------------------------------
.dseg

.org $0060

LuxBuf:  .byte 8 ; 8 Bytes for Light Values




Link: http://www.avrfreaks.net/index.php?module=...ect&item_id=588

Guest- 06-24-2006
see this sun tracker, one without any microcontroller
http://www.geocities.com/njbibin/robotics/suntracker.html


Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.