#define __SFR_OFFSET 0
#include <avr/io.h>
.section .bss
azt: .ds.b 34 // 31 Byte Anoden-Zeit-Tabelle für Compare-ISR
azt2: .ds.b 34 // Anoden-Zeit-Tabelle für Vorberechnung
repid3: .ds.b 1 // Report-ID (=3), auch als Guard verwendbar?
lht: .ds.b 100 // 100 Byte LED-Helligkeits-Tabelle
// Diese Tabelle ragt in die 100h-Adressen hinein;
// die 8-bit-Programmlogik funktioniert gerade so
#define DEFD r8 // Default für PortD
#define DEFB r9 // Default für PortB
#define CurKatode r15 // Gerade aktiver Katodentreiber (0..9)
#define ONES r14 // immer 0xFF
#define ZERO YH // YH ist immer 0 (r1 nicht!), Y zeigt in azt
// ebenfalls „giftig“: R2 (ISR-Temp), R12(=40h), R13(für SREG)
.section .text
rjmp init
reti
reti
/*
Timer2-Vergleich: Anode (oder auch Katode) weiterschalten, max. 32 Takte!
vorgehalten:
R2, R3 = wird von der ISR zerstört
R8, R9 = Konstanten
R14 = immer 0xFF
Y = Zeiger in Anoden-Zeit-Tabelle (Y ist bei avr-gcc der Stapelrahmenzeiger)
Die Anoden-Zeit-Tabelle "azt" besteht aus 1 bis 11 Einträgen der Form
1 Byte für nächsten Output-Compare-Wert, regulär maximal 230
der erste Eintrag enthält zusätzlich zum Schalten von Katoden:
1 Byte für eine Katode (von 6) an PortC
1 Byte für 4 Anoden (PortD) )
1 Byte für 6 Anoden (PortB) ) 0-Bits für eingeschaltete Anoden, Katodenbits unverändert
Der Wert 255 für den Output-Compare-Wert markiert das Ende der Tabelle,
dann noch eingeschaltete Anoden lassen die LEDs bis zum Ende leuchten.
Der maximale Wert ergibt sich daraus, dass für das Kopieren
der nächsten <azt>-Tabelle Zeit erforderlich ist.
Damit kann der Output-Compare-Interrupt sehr schnell arbeiten.
Folgende Tricks werden für kürzeste Latenz eingesetzt:
* ISR-Start direkt in Interruptvektortabelle, kein Sprung, -2 Takte
* ISR wird bei OCR2=0xFF gleichzeitig mit Überlaufinterruptflag gestartet,
dies wird ausgewertet zum Katode weiterschalten.
* Eine extra Overflow-ISR ist umständlicher, weil man den
Compare-Interrupt irgendwie totlegen müsste
* Kein Register-Push/Pop, vorgehaltene „globale“ Register
* Keine Veränderung der Flags
*/ //(4) ISR-Aufruf (Latenz)
ld r2,Y+ // alle folgenden Befehle ändern SREG nicht!
out OCR2,r2 //(3) nächster Interruptzeitpunkt
in r2,TIFR // (leider nicht bitadressierbar!)
sbrs r2,6 // Überlauf-Interrupt gleichzeitig?
rjmp no_ovl //(3-4)
out TIFR,r12 // (1) anhängigen Interrupt löschen
out PORTD,DEFD // alle Anoden (und Katoden) ausschalten
out PORTB,DEFB // (2) (Geisterbilder vermeiden)
ld r2,Y+
out PORTC,r2 // (3) Neue Katode
no_ovl: ld r2,Y+
out PORTD,r2 //(3) 4 Anoden (mit Katode)
ld r2,Y+
out PORTB,r2 //(3) 6 Anoden (mit Katode)
in r2,OCR2
cpse r2,ONES //(2) Vergleich mit 0FFh
reti //(4)
//Summe (28) bzw. (23)
ldi YL,azt
sbic SPCR,0
rjmp reent
sbi SPCR,0 // Reentranz-Sperre
//push r13
again: cbi SPCR,1
in r13,SREG
sei
//Da bei (langen) USB-Interrupts der Y-Zeiger temporär wild herumgeistern kann,
//sollten ungenutzte Speicherbereiche mit 0FFh gefüllt werden.?
//Mit Flackereffekten muss man leben.
rcall calc_azt //(viele Takte)
out SREG,r13
sbic SPCR,1
rjmp again
//pop r13
cbi SPCR,0
reti
reent: sbi SPCR,1 // "bitte <calc_azt> wiederholen"
reti
//Anoden-Tabelle, low-aktiv, für PortD und PortB, ungenutzte Bits 1
ant: .byte 0xEF,0xFF
.byte 0xDF,0xFF
.byte 0xBF,0xFF
.byte 0x7F,0xFF
.byte 0xFF,0xFE
.byte 0xFF,0xFD
.byte 0xFF,0xFB
.byte 0xFF,0xF7
.byte 0xFF,0xEF
.byte 0xFF,0xDF
//Katoden-Tabelle, high-aktiv, für PortC, PortD und PortB, ungenutzte Bits LOW
kat: .byte 0x00,0x00,0x80, 0x00,0x00,0x40
.byte 0x00,0x02,0x00, 0x00,0x01,0x00
.byte 0x20,0x00,0x00, 0x10,0x00,0x00
.byte 0x08,0x00,0x00, 0x04,0x00,0x00
.byte 0x02,0x00,0x00, 0x01,0x00,0x00
//Exponentialfunktion (generiert durch exp.awk) für LED-Pulsweite, 32 Stufen
exp: .byte 0,1,2,3,5,6,8,10,12,15,17,20,24,27,32,36
.byte 42,47,54,61,70,79,89,100,113,127,143,161,181,203,227,255
//Kode-Byte-Adresse hier < 256!! Im Listing kontrollieren!
calc_azt: //darf R2, R3 und Y nicht verändern
push r0
push r1
// push r3
// push r4
// push r5
// push r6
// push r7
// push r10
push XL
push XH
push ZL
push ZH
//Daten aus azt2 nach azt vorkopieren
//memcpy(azt,azt2,sizeof(azt));
ldi ZL,lo8(azt)
ldi ZH,hi8(azt)
ldi XL,lo8(azt2)
ldi XH,hi8(azt2)
ca0: ld r0,X+
st Z+,r0
cpi XL,lo8(repid3)
brne ca0
//const BYTE *p=lht;
inc XL
//BYTE *q=azt2;
#define q r10
mov q,ZL
//Berechnen der Anoden-Zeit-Tabelle "azt" anhand der LED-Helligkeits-Tabelle "lht"
//CurKatode++;
inc CurKatode
//if (CurKatode>=10) CurKatode=0;
ldi ZL,10
cp CurKatode,ZL
brcs ca9
clr CurKatode
ca9:
//p+=10*CurKatode;
mul CurKatode,ZL
add XL,r0
//BYTE u=0; // untere Grenze
#define u r6
clr u
//bool f=false;
clt
//do{
ca1:
// BYTE o=255; // obere Grenze
#define o r7
mov o,ONES
// WORD m=0xFC3F; // Bit-Maske für Anoden
#define mD r4
#define mB r5
movw mD,DEFD
ldi ZL,ant // im AVR-GCC ist dies die Byte-Adresse
// for (BYTE i=0; i<10; i++) {
ca2:
// BYTE t=exp[p[i]>>3];
#define t r3
mov r0,ZL
ld t,X+ // LED-Helligkeit
ldi ZL,exp // das kürzere "subi ZL,-exp" kann der Linker nicht auflösen
add ZL,t
lpm t,Z // Exponentialfunktion anwenden, t = Zeit
mov ZL,r0
lpm r0,Z+ // Zugehörige Anoden-Maske für PortD
lpm r1,Z+ // Zugehörige Anoden-Maske für PortB
// if (t>u) { // Minimum suchen, welches größer als "u" ist
cp u,t
brcc ca3
// m&=ant[i]; // Anode zuschalten (wenn u<t)
and mD,r0 // Gesamt-Maske bearbeiten: PortD
and mB,r1 // dito für PortB
// if (o>t) o=t; // Compare-Match-Wert ermitteln
cp t,o
brcc ca3
mov o,t // Minimum finden
// }
ca3: cpi ZL,kat // alle 10 LEDs (Anoden) absuchen
brne ca2
sbiw XL,10 // Zeiger auf LED-Helligkeiten (lht) zurücksetzen, XH wieder 0
// }
add ZL,CurKatode // Z zeigt auf Katoden-Tabelle, passende Katode adressieren
add ZL,CurKatode
add ZL,CurKatode
mov r1,XL
mov XL,q
// *q++ = o==255 ? o : o-1;
mov r0,o
cpse r0,ONES
dec r0
st X+,r0 // nächster Compare-Wert
// if (!f) *q++=katC[CurKatode];
lpm r0,Z+ // schon mal Katodenbit für PortC laden
brts ca4 // Erster Durchlauf?
st X+,r0 // Katoden umschalten (Anoden sind aus)
ca4:
// *q++=LOWORD(m)|katD[CurKatode];
// *q++=HIWORD(m)|katB[CurKatode];
lpm r0,Z+
or mD,r0 // Anodenwert für PortD mit Katodenmaske
st X+,mD
lpm
or mB,r0 // dito für PortB
st X+,mB
mov q,XL
mov XL,r1
// u=o;
mov u,o
// f=true;
set // nächste Runde kennzeichnen (ohne Header)
//}while (u<255);
cp u,ONES
brne ca1
#undef u
#undef o
#undef mB
#undef mD
#undef t
pop ZH
pop ZL
pop XH
pop XL
// pop r10
// pop r7
// pop r6
// pop r5
// pop r4
// pop r3
pop r1
pop r0
ret
/******************
* Unterprogramme *
******************/
.section .bss
seed: .ds.b 2
.section .text
/*=========================
= Zufallszahl ermitteln =
=========================*/
// PE: R24 = gewünschtes Maximum (inklusive)
// PA: R16 = Ergebnis, <= R24
// VR: R0,R1,R16,R17,R18=0,R25
random0:
lds r0,seed
lds r1,seed+1
ra0: clr r16
mov r18,r24
ra1: mov r25,r1
andi r25,0xB4 // gewünschte Bits
clr r17
ra2: eor r25,r17 // Schleife bildet ungerade Parität im MSB
mov r17,r25
andi r25,0x80 // MSB stehen lassen
add r17,r17 // linksschieben
brne ra2 // Schleife solange 1-Bits vorhanden
rol r25
rol r0
rol r1
rol r16 // Ergebnisbits
lsr r18 // Rundenzähler als Schieberegister
brne ra1 // nächstes Bit
cp r24,r16 // Ergebnis zu groß?
brcs ra0 // noch einmal würfeln
storeseed:
sts seed,r0
sts seed+1,r1
ret
// Speicher (RAM) durchsuchen
// PE: Z = Adresse
// R1 = Länge 1..256 (0 = 256)
// R16 = Vergleichsbyte
// PA: ZF=1 wenn gefunden, sonst ZF=0
// Z zeigt hinter gefundene Position
// R1 = verbleibende Bytes
// VR: R0,R1,Z
repne_scasb:
ld r0,Z+
cp r0,r16
breq sce // raus mit ZF=1
dec r1
brne repne_scasb
clz
sce: ret
// PE: R24 = gewünschtes Maximum
// R19 = gewünschtes Minimum
// Z = RAM-Zeiger auf vorhandene Zufallszahlen, die NICHT herauskommen sollen
// R20 = Länge RAM-Bereich, 0..255
// PA: R16 = Ergebnis
// VR: R0,R1,R16,R17,R18=0,R24,R25
random2:
sub r24,r19
ra4: rcall random0
add r16,r19
tst r20
breq ra5 // Nicht suchen
mov r1,r20
push ZL
push ZH
rcall repne_scasb // Vorkommnis?
pop ZH
pop ZL
breq ra4 // noch einmal würfeln
ra5: ret
.section .eeprom
bootloader_magic:
.byte 0
osccal_vorgabe:
.byte 0
bilder_vorgabe: // wird in RAM kopiert
.byte 0x00,0x00,0x01,0x00,0x0B,0x00,0x09,0x03,0x0A,0x03
.byte 0x0E,0x0E,0x0D,0x03,0x01,0x0B,0x03,0x0A,0x0C,0x0C
.byte 0x0D,0x00,0x00,0x08,0x09,0x03,0x00,0x0A,0x0E,0x0B
.byte 0x01,0x03,0x0E,0x09,0x08,0x0A,0x0B,0x00,0x00,0x0A
.byte 0x00,0x02,0x00,0x02,0x0D,0x01,0x0C,0x06,0x0C,0x0B
.byte 0x02,0x08,0x09,0x03,0x01,0x0A,0x0C,0x06,0x0D,0x02
.byte 0x0E,0x0D,0x08,0x01,0x04,0x02,0x06,0x06,0x06,0x01
.byte 0x0D,0x09,0x04,0x07,0x05,0x04,0x05,0x06,0x05,0x00
.byte 0x09,0x04,0x07,0x07,0x07,0x05,0x0F,0x05,0x0F,0x00
.byte 0x04,0x00,0x04,0x07,0x0F,0x01,0x02,0x0F,0x0F,0x02
/* Low-Nibble = Sternbild-Zuordnung, High-Nibble = relative Helligkeit?
0 = frei
1 = Orion
2 = großer Bär
3 = kleiner Bär
4 = großer Hund
5 = Himmels-W
6 = Schwan
7 = Kreuz des Südens
8 = Zwillinge
9 = Schlange
A = Löwe
B = Stier
C = Perseus
D = Pegasus
E = Zentaur
F = Skorpion
*/
.section .bss
bilder: .ds.b 100 // Sternbild-Tabelle aus EEPROM
phase: .ds.b 2 // Durchlaufender Zähler
bild: .ds.b 1 // Gezeigtes Sternbild
bild0: .ds.b 1 // vorhergehendes Bild (zum Ausblenden bei kleinen Phasenwerten)
quasar: .ds.b 8 // Stern-Indizes der Quasare
super: .ds.b 1 // Stern mit Supernova
quasph: .ds.b 8 // Phasen für Quasare (Frequenz gleich)
quasi: .ds.b 1 // Index für auszuwechselnden Quasar
disko: .ds.b 1 // Phase für Disko, Helligkeitssteller = Freuenz
flimm: .ds.b 16 // 16 Flimmer-Frequenzen (Phasen)
langs: .ds.b 16 // 16 langsame Frequenzen (Phasen)
wasser: .ds.b 1 // Richtung der Wellenfront (0..7);
prog1: .ds.b 1 // Programm-Nummer, s.u.
prog2: .ds.b 1 // Sekundär-Programm
teil2: .ds.b 1 // Anteil der Helligkeit bei der Mischung (0..3)
repid1: .ds.b 1 // Report-ID = 1 (konstant, für USB)
hell: .ds.b 1 // Helligkeits-Wert 0..31 (vom Potenziometer)
repid2: .ds.b 1 // Report-ID = 2 (konstant, für USB)
prog: .ds.b 1 // Programm-Wert 0..31 (vom 2. Potenziometer)
.section .text
/*=========================
= 8 Animationsprogramme =
=========================*/
// PE: R24 = Index 0..99
// PA: R0 = Helligkeit 0..31
// VR: R0,R1,R16,R17,R24,R25,Z
// 0 = Gleichmäßige Helligkeit
Prog0H:
lds r0,hell
ret
// 1 = Sternbilder zeigen
Prog1H:
lds r16,phase // R16 = Anteil
cpi r16,64
brcs p1h1
ldi r16,64
p1h1: ldi ZL,lo8(bilder)
ldi ZH,hi8(bilder)
add ZL,r24
adc ZH,ZERO
ld r0,Z
lds r17,bild
eor r17,r0
andi r17,0x0F // R17!=0: nicht aktuelles Sternbild
breq p1h2
cpi r16,64
brcc ret0
lds r17,bild0
eor r17,r0
andi r17,0x0F // R17!=0: nicht vorheriges Sternbild
brne ret0
neg r16
subi r16,-63
p1h2: ldi r17,6 // Schiebeoperationen
retant: lds r0,hell
mul r0,r16
reta1: lsr r1
ror r0
dec r17
brne reta1
ret
ret0: clr r0
ret
// 2 = Quasare
Prog2H:
ldi ZL,lo8(quasar)
ldi ZH,hi8(quasar)
ldi r16,8
mov r1,r16
mov r16,r24
rcall repne_scasb
brne ret0 // kein Quasar
ldd r0,Z+(quasph-quasar-1) // passende Phase
lds r1,phase
add r0,r1
mov r16,r0 // R16 = Anteil
andi r16,0x07 // 0..7
sbrc r0,3
neg r16
sbrc r0,3
sbci r16,-8 // 8..1
ldi r17,3
rjmp retant
// Programmverteiler (eingeschoben wegen Sprungdistanzen)
// PE: R25 = Programmnummer 0..7
// X = Zeiger in lht
// PA: R0 = Helligkeit 0..31
// VR: R0,R1,R16,R17,R24,R25,Z
ProgV:
mov r24,XL
subi r24,lo8(lht) // 0..99
cpi r25,1
breq Prog1H
cpi r25,2
breq Prog2H
cpi r25,3
breq Prog3H
cpi r25,4
breq Prog4H
cpi r25,5
breq Prog5H
cpi r25,6
breq Prog6H
cpi r25,7
breq Prog7H
rjmp Prog0H // alles andere (bspw. im Fehlerfall)
// 3 = Supernova
// Problem: - Sollte auch gemischt mit maximaler Helligkeit leuchten
Prog3H:
lds r0,super
cp r0,r24
breq p3h1
lds r0,hell
lsr r0 // halbe Grundhelligkeit
ret
p3h1: lds r0,phase
lds r1,phase+1
lsr r1
ror r0
rcall r0lsr3 // jetzt 0..31
ldi r16,31
sbrc r1,0
mov r0,r16 // Maximum (2. Hälfte der Phase)
ret
// 4 = Disko (alle LEDs gleich; Frequenz statt Helligkeit einstellbar)
Prog4H:
lds r0,disko
com r0 // Fallende Intensität
r0lsr3: lsr r0
lsr r0
lsr r0 // jetzt 0..31
ret
// Hilfsprozedur für Flimmern und Langsam: Determinierte Zufallszahl 0..15
// aus Index (R24, 0..99) ermitteln (svw. ein Hash-Wert)
makeposrand:
ldi ZL,lo8(bilder)
ldi ZH,hi8(bilder)
add ZL,r24
adc ZH,ZERO
ld r0,Z
eor r24,r0 // 16 Zahlen, determiniert aber verstreut verteilt
ret
// 5 = Flimmern (schnelles Auf und Ab der Helligkeit in 16 Frequenzen)
Prog5H:
rcall makeposrand
andi r24,0x0F // R24 = posrand
ldi ZL,lo8(flimm)
ldi ZH,hi8(flimm)
p5h1: add ZL,r24
adc ZH,ZERO
ld r0,Z
sbrc r0,7
neg r0 // 0..128
mov r16,r0
ldi r17,7
rjmp retant // Zwei Flanken
// 6 = Langsam (sonst wie Flimmern)
Prog6H:
rcall makeposrand
com r24
andi r24,0x0F // R24 = posrand
ldi ZL,lo8(langs)
ldi ZH,hi8(langs)
rjmp p5h1
// 6 = Wasserfront (hell oder dunkel je nach Helligkeitssteller)
// Zurzeit Implementierung anhand Matrixposition, später ggf. mit
// Positionsangaben-Array im EEPROM ähnlich Sternbild-Vorgabe
Prog7H:
mov r25,ONES
p7h0: inc r25
subi r24,10 // Division durch 10
brcc p7h0
subi r24,-10 // Korrektur, R24 = Rest, R25 = Ergebnis
lds r16,wasser
cpi r16,1 // R24 = logpos
brne p7h1
mov r24,r25 // gerade (0..9)
p7h1: cpi r16,2
brne p7h2
add r24,r25 // diagonal (0..18)
p7h2: cpi r16,3
brne p7h3
sub r24,r25 // andersherum diagonal (-9..9)
p7h3: sbrc r16,2
neg r24 // Gegenrichtung (-18..18 möglich)
clr r17 // R17 = h (Helligkeit, 0 = keine Aktivität)
lds r16,phase
cpi r16,64
brcc p7h4 // keine Aktivität
mov r17,r16
subi r17,32 // -32 .. 31
add r17,24 // -50 .. 49
sbrs r17,7
com r17 // Bei Null maximale Helligkeit, neg. Betrag bilden (-50..0)
subi r17,-31 // -19 .. 31
sbrc r17,7
clr r17 // alles Negative wegschneiden
p7h4: lds r16,hell
cpi r16,16
brcs p7h5
neg r17 // hell: dunkle Welle
subi r17,-31
cp r17,r16
brcs p7he
rjmp p7h6
p7h5: cp r17,r16
brcc p7he
p7h6: mov r17,r16
p7he: mov r0,r17
ret
/*============================
= Verwaltung der Animation =
============================*/
PeriodicAction:
ldi ZL,lo8(phase)
ldi ZH,hi8(phase)
ldd r16,Z+prog-phase
cpi r16,31
brcc pa1 // variable Programme am Endanschlag
mov r17,r16
lsr r17
lsr r17 // 0..7
std Z+prog1-phase,r17 // festes Programm
inc r17
andi r17,7 // 0..7
std Z+prog2-phase,r17
andi r16,3 // 0..3
std Z+teil2-phase,r16 // fester Anteil
pa1: ldi XL,lo8(lht)
ldi XH,hi8(lht)
ldd r23,Z+teil2-phase // R23 = teil2 (0..3)
ldi r22,4
sub r22,r23 // R22 = teil1 (4..1)
pa2: lds r25,prog1 // ab hier ist Z versaut
rcall ProgV
mul r0,r22
tst r23
breq pa3
mov r19,r0 // R19 = h (retten)
lds r25,prog2
rcall ProgV
mul r0,r23
add r0,r19
pa3: lsr r0
lsr r0 // hier evtl. runden!!
st X+,r0
cpi XL,lo8(lht+100)
brne pa2
// jetzt Zähler aktualisieren
ldi ZL,lo8(phase)
ldi ZH,hi8(phase)
ld r24,Z
ldd r25,Z+1
adiw r24,1 // vorrücken
std Z+1,r25
st Z,r24
movw r22,r24 // R23:R22 = Phase
adiw ZL,bild-phase
tst r22
brne pa4
ld r21,Z
ldi r24,15
ldi r19,1
ldi r20,2 // nicht das letzte und vorletzte Bild
rcall random2
st Z,r16
std Z+bild0-bild,r21
pa4: adiw ZL,quasar-bild
mov r16,r22
andi r16,63
brne pa5
ldi r24,99
ldi r19,0
ldi r20,9 // keiner der 8 Quasare und nicht der Supernova-Stern
rcall random2
ldd r21,Z+quasi-quasar
movw XL,ZL
add XL,r21
adc XH,ZERO
st X,r16
ldi r24,255
rcall random0
adiw XL,quasph-quasar // X ist bereits indiziert
st X,r16
inc r21
andi r21,7
std Z+quasi-quasar,r21
pa5: mov r16,r23
andi r16,hi8(1023) // = 3
or r16,r22
brne pa6
ldi r24,99
ldi r19,0
ldi r20,9 // keiner der 8 Quasare und nicht der Supernova-Stern
rcall random2
std Z+super-quasar,r16
// Im automatischen Modus jetzt Programm wechseln (mit Übergang)
ldd r16,Z+prog-quasar
cpi r16,31
brcs pa6
adiw ZL,prog2-quasar
ldi r19,0
ldi r20,1
pa7: ldi r24,7
rcall random2
cpi r16,4 // Disko ausschließen
breq pa7
st Z,r16 // neues Programm (Übergang später)
pa6: ldi ZL,lo8(phase)
ldi ZH,hi8(phase) // Z war versaut hier
// Disko-Phase aktualisieren
ldd r17,Z+hell-phase
subi r17,-16 // Stellverhältnis 3:1
ldd r16,Z+disko-phase
add r16,r17
std Z+disko-phase,r16
// Flimmer-Phasen aktualisieren
adiw ZL,flimm-phase
mov r17,3 // 3..19
pa8: ld r16,Z
add r16,r17
st Z+,r16
inc r17
cpi ZL,lo8(flimm+16)
brne pa8
// Langsam-Phasen aktualisieren (Z steht schon richtig!)
mov r16,r22
andi r16,15
brne pa10
pa9: ld r16,Z
mov r17,ZL
subi r17,lo8(langs)
lsr r17
lsr r17 // 4 Geschwindigkeiten
inc r17
add r16,r17
st Z+,r16
cpi ZL,lo8(langs+16)
brne pa9
sbiw ZL,16
// Wasser-Richtung ändern (Z steht auf "langs")
pa10: tst r22
brne pa11
adiw ZL,wasser-langs
ldi r24,7
ldi r19,0
ldi r20,1
rcall random2
st Z,r16
sbiw ZL,wasser-langs
pa11: ldd r16,Z+prog-langs
cpi r16,31
brne pae
mov r16,r22
andi r16,0x0F
brne pae
swap r22
andi r22,0x0F
swap r23
andi r23,0x30
or r22,r23 // R22 = (phase&1023)>>4
cpi r22,4
brcc pae
inc r22
andi r22,3 // 1-2-3-0 (Intensität des neuen Motivs)
std Z+teil2-langs,r22
brne pae
ldd r16,Z+prog2-langs
std Z+prog1-langs,r16 // neues Motiv = erstes Motiv
pae: ret
// PE: R0 = neuer <hell>-Wert, von außen oder per Poti
// PA, VR: -
SetHell:
sts hell,r0
cbi SPCR,2 // Programmlauf freigeben
ret
// PE: R0 = neuer <prog>-Wert, von außen oder per Poti
// PA, VR: -
SetProg:
sts prog,r0
cbi SPCR,2 // Programmlauf freigeben
ret
/*==================================================
= A/D-Wandler (Potenziometerstellungen) abfragen =
==================================================*/
// Abfrage 8-bit-A/D-Wandler mit exponentieller Mittelung
// PE: R24 = vorhergehender Wert (0..FF)
// PA: R0 = neuer Wert (0..FF)
// VR: R0,R1,R16
MeanAdcVal:
ldi r16,7
mul r16,r24
in r16,ADCH
add r0,r16
adc r1,ZERO
ldi r16,4
add r0,r16
adc r1,ZERO
lsr r1
ror r0
lsr r1
ror r0
lsr r1
ror r0
ret
// PE: Z = Speicherzelle vorhergehender A/D-Wert (8 bit)
// PA: ZF=0 bei relevanter Änderung
// R0 = neuer Wert (0..31, also 5 bit)
// VR: R0,R1,R16,R24
ChangedAdcVal:
ld r24,Z
rcall MeanAdcVal
st Z,r0
eor r24,r0
rcall r0lsr3
andi r24,0xF8 // Wesentliche Bits geändert?
ret
.section .bss
adcv6: .ds.b 1
adcv7: .ds.b 1
.section .text
// Abfrage der A/D-Kanäle 6 und 7 (wechselweise)
// Bei Änderung wird SetHell() bzw. SetProg() aufgerufen
// PE: - (A/D-Wandler muss initialisiert und gestartet sein)
// PA: -
// VR: R0,R1,R16,R24,Z
AdcPoll:
sbis ADCSRA,4 // ADIF
ret // Konversion in Arbeit
ldi ZL,lo8(adcv6)
ldi ZH,hi8(adcv6)
sbis ADMUX,0
rjmp ap1
cbi ADMUX,0 // umschalten auf ADC6
adiw ZL,adcv7-adcv6
rcall ChangedAdcVal
breq ap2
rcall SetProg
rjmp ap2
ap1: sbi ADMUX,0 // umschalten auf ADC7
rcall ChangedAdcVal
breq ap2
rcall SetHell
ap2: sbi ADCSRA,6 // Konversion starten
ret
/**********
* EEPROM *
**********/
// Liest EEPROM-Speicherzelle
// PE: X = EEPROM-Adresse
// PA: R0 = EEPROM-Datenbyte (auch EEDR)
EepromRead:
out EEARH,XH
out EEARL,XL
sbi EECR,0
in r0,EEDR
ret
// Lädt Vorgaben aus EEPROM: OSCCAL und Sternbild-Zuordnungen
// PE,PA: -
// VR: R0,R16,X,Z
EepromLoad:
ldi XL,lo8(osccal_vorgabe)
ldi XH,hi8(osccal_vorgabe)
rcall EepromRead
tst r0
breq ee1
cp r0,ONES
breq ee1
out OSCCAL,r0
ee1: ldi r16,100
ldi ZL,lo8(bilder)
ldi ZH,hi8(bilder)
ee2: adiw XL,1
rcall EepromRead
st Z+,r0
dec r16
brne ee2
ret
/*************************************
* Initialisierung und Hauptschleife *
*************************************/
init:
// RAM löschen
clr ZERO
ldi ZH,0
ldi ZL,0x60
i1: st Z+,ZERO
cpi ZL,0x5F
brne i1
cpi ZH,0x04
brne i1
// Stack initialisieren
out SPH,ZH
out SPL,ZL
// Konstanten initialisieren
clr ONES
dec ONES
ldi ZL,0x40
mov r12,ZL
ldi ZL,0x3F // Defaults für PORTB und PORTD
mov DEFB,ZL
#ifdef ENABLE_USB
ldi ZL,0xFC
#else
ldi ZL,0xF0
#endif
mov DEFD,ZL
ldi YL,azt
ldi ZL,1
sts repid1,ZL
ldi ZL,2
sts repid2,ZL
ldi ZL,3
sts repid3,ZL
// Pseudozufallsgenerator initialisieren
mov r1,ONES // R1:R0 != 0 garantieren
rcall storeseed // r0 sollte zufällig sein, oder??
// ISR initialisieren (CurKatode muss nicht initialisiert werden)
rcall calc_azt // Erste Tabelle berechnen
rcall calc_azt // Zweite Tabelle berechnen
out OCR2,ONES // erster Interrupt bei Überlauf
ldi ZL,0x03 // Vorteiler 32
out TCCR2,ZL
ldi ZL,0x80 // nur Compare-Interrupt
out TIMSK,ZL
out MCUCR,ZL // Sleep aktivieren
ldi ZL,0x0D // CTC-Modus, Teiler=1024
out TCCR1A,ZERO
out TCCR1B,ZL
ldi ZL,lo8(320) // 4 * 10 * 32 * 256 / 1024: aller 4 Zyklen
ldi ZH,hi8(320)
out OCR1AH,ZH
out OCR1AL,ZL
// Ports initialisieren
out PORTB,DEFB
out PORTC,ZERO
out PORTD,DEFD
out DDRB,ONES
out DDRC,ONES
#ifdef ENABLE_USB
ldi ZL,0xF3
out DDRD,ZL
#else
out DDRD,ONES
#endif
// ADU für die 2 Potis initialisieren (5 bit werden verwendet)
ldi ZL,0x66 // potenziometrisch, linksbündig, ADC6
out ADMUX,ZL
ldi ZL,0xC7 // Start, Taktteiler maximal
out ADCSRA,ZL
#if 1
#if 0
// Debug-Beispiel laden
ldi ZL,lo8(lht)
ldi ZH,hi8(lht)
ldi r16,100
fillh: //mov r17,r16
ldi r17,1
cpi r16,11
brcc f1
ldi r17,1
f1: st Z+,r17
dec r16
brne fillh
mov r0,r17
rcall SetHell
#else
aw1: sbis ADCSRA,4 // ADIF
rjmp aw1
rcall AdcPoll // SetProg() auslösen
aw2: sbis ADCSRA,4 // ADIF
rjmp aw2
rcall AdcPoll // SetHell() auslösen
#endif
rcall EepromLoad
sei
mloop: wdr
mw1: sleep
in r16,TIFR
sbrs r16,4 // Timer1-Umlauf? (CTC)
rjmp mw1
ldi r16,0x10
out TIFR,r16 // Bit löschen
// sbi PORTD,2
rcall AdcPoll
rcall PeriodicAction
// cbi PORTD,2
rjmp mloop
#else
ldi ZL,0x10
sts lht+0,ZL
ldi ZL,30
sts lht+2,ZL
sts lht+8,ONES
sts lht+7,ONES
sei
mainloop:
w1: sleep
tst CurKatode
brne w1
w2: sleep
tst CurKatode
breq w2
inc r9
inc r9
sts lht+22,r9
sbrs r10,0 //Richtungsbit
rjmp d1
dec r10
breq d2 //aufwärts
d3: dec r10
rjmp d2
d1: inc r10
inc r10
breq d3 //abwärts
d2: sts lht+42,r10
sts lht+43,r10
in r0,ADCSRA
rcall OutBits
sbis ADCSRA,4 //Konversion fertig?
rjmp mainloop //nein
in r0,ADCH
sbis ADMUX,0
rjmp a1
cbi ADMUX,0 //umschalten auf ADC6
sts lht+30,r0
sts lht+31,r0
rjmp a2
a1: sbi ADMUX,0 //umschalten auf ADC7
sts lht+20,r0
sts lht+21,r0
a2: sbi ADCSRA,6 //Konversion starten
rjmp mainloop
OutBits:
ldi ZL,lo8(lht+50)
ldi ZH,hi8(lht+50)
ldi XH,8
t1: ldi XL,0x80
and XL,r0
st Z+,XL
add r0,r0
dec XH
brne t1
ret
#endif
Vorgefundene Kodierung: UTF-8 | 0
|