PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Tutorial für alle Assembler-Anfänger _



Seiten : [1] 2 3

toeoe
07.08.2005, 16:42
Hallo,

sry, dass ich nochmal nerve, aber ich probier schon die ganze Zeit, sone Art "Lichtschalter" hinzubekommen.
Vorneweg erstmal: Ich hab das myAvr-Board 1.4
Hab einen Taster mit PortD.2 und einen Taster mit PortD.3 verbunden. Dann hab ich noch eine LED mit PortB.2 verbunden. Wenn ich den Taster1 (PortD.2) drücke, soll die LED ausgehen und wenn ich Taster2 (PortD.3) drücke, soll die LED angehen. Ich bekomms einfach net, vlt. könnt ihr mir weiterhelfen, das ganze möcht ich gern mit Assembler realisieren.

Hier mein Code:

.include "m8def.inc"

.def temp = r16

ldi temp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, temp
ldi temp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, temp

ldi temp, 0b00000000
out DDRD, temp ;PortD als Eingang
ldi temp, 0b11111111
out DDRB, temp ;PortB als Ausgang
out PortD, temp ;Pullups von PortD aktivieren

main:
sbic PIND, 2 ;Liegt an D.2 low an? Wenn nicht, dann nächsten Befehl überspringen
sbi PortB, 2 ;Wenn ja, dann B.2 auf high setzen

sbic PIND, 3 ;Liegt an D.3 low an? Wenn nicht, dann nächsten Befehl überspringen
cbi PortB, 2 ;Wenn ja, dann B.2 auf low setzen

rjmp main ;Endlosschleife


Ich denk mal, Geübte steigen durch den Code durch.

Das was er jetzt macht: Am Anfang leuchtet die LED ganz schwach (soll aber ganz aus sein). Wenn ich auf einen Taster drücke, geht sie aus (wenn ich ihn aber wieder loslasse, ist sie wieder schwach an) und wenn ich auf den anderen Taster drücke, leuchtet sie stark (geht dann aber auch wieder auf schwach, wenn ich den Taster loslasse :( )

Gruß
Thomas

Jahn Kohlhas
07.08.2005, 16:47
hast du dich schon mal mit dem thema "entprellen" beschäftigt?

toeoe
07.08.2005, 16:54
Nee, noch nie gehört :o

[edit]
Ahh, du meinst, das der Taster nicht gleich zum stehen kommt, sondern noch ein wenig "hüpft", wie ein Ball, den man fallen lässt?
Hmm..aber was das genau mit meinem Problem zu tun hat, versteh ich leider noch nicht so ganz.

Jahn Kohlhas
07.08.2005, 18:20
ja genau das meine ich... auf den ersten blick müsste das gehen was du da geschrieben hast... kann es aber im moment nicht überprüfen :-(

toeoe
07.08.2005, 18:22
Naja, das was ich ja am meisten nicht verstehe ist, warum am Anfang die LED schwach leuchtet?

klecksinger
07.08.2005, 18:48
SBIC bedeutet, den nächsten Befehl über springen falls der entsprechende Pin auf 0 ist, d.h. dein Schalter gedrückt ist.
Wenn du keinen Schalter drückst, wird die LED abwechselnd schnell an un d ausgeschaltet : LED leuchtet schwach.

Du solltest es anstatt mit SBIC mal mit SBIS versuchen, dann sollte es klappen.

Gruss Klaus

toeoe
07.08.2005, 19:07
Ahh, so gehts, danke :)
Ich mach mir auch grad Gedanken, wie ich das ganze auf nur einen Schalter mache, also drücken, dann LED an, nochmal drücken, LED aus. Am besten wäre es, wenn ich mir ne Variable anlege und dort speicher, ob LED an oder aus ist, aber wie ich Variablen anlege, weiß ich leider nicht.
Aber ich denk, da gibt es auch nen einfacheren Weg, oder?
So wie:


if Taste gedrückt
if PortB.2 = 1
dann PortB.2 = 0

So wäre dann wohl der Pseudecode.
Bitte noch keinen Code posten, brauch nur nen Denkanstoß ;)

izaseba
07.08.2005, 19:19
Hallo, als Variablen in Assembler nehme ich einfach einen freien Register darin kannst Du die bits mit sbr und cbr setzen oder löschen.
Mit sbrs springst Du wenn bit gesetzt alternativ sbrc wenn gelöscht.

Achtung sbr bzw. cbr erwarten ein k255 als wert, also setzen mit z.B sbr test,(1<<sperre)
und sbrs , sbrc erwarten nur ein b7 also sbrs test,sperre
Gruß Sebastian

klecksinger
07.08.2005, 19:20
Mit nur einem Schalter bekommst du Probleme wegen des Schalterprellens. Dieses Problem kann softwaremässig nur mit einem Timer gelöst werden, der innerhalb einer vorgegebenen Zeit (ca. 0,5 Sekunden) nach dem Drücken des Schalters eine weitere Abfrage des Schalters verhindert. Da ich eigentlich nicht in Assembler programmiere, kann ich dir hierfür auch keine Hilfe bieten. In C hatte ich das Problem aber schon mal so gelöst.
Gruss Klaus

toeoe
07.08.2005, 19:24
Axo, hab mich hier im Forum auch schonmal schlau gemacht wegen dem Prellen. Ist ziemlich kompliziert zu lösen mitm Timer. Najo, muss ich mal schaun, trotzdem Danke an euch.

izaseba
07.08.2005, 19:26
Nochwas,
Du brauchst keine Variable, um zu speichern, ob die Led an oder aus ist,
das kannst Du ja aus dem Pin auslesen, und entsprechend toggeln,
was Du Dir eher speichern solltest, ist ob der taster schon mal losgelassen wurde,
oder nicht.
Und warum das so ist, wirst Du schon rausfinden, wenn Du am probieren bist :D

Ich sag nur, die Aufgabe ist schwerer als man denkt.

Gruß Sebastian

izaseba
07.08.2005, 19:28
Ist ziemlich kompliziert zu lösen mitm Timer

Timer sind einfacher, als man denkt,
Aber zum entprellen wirklich sehr gut zu gebrauchen

toeoe
07.08.2005, 19:36
Jo, ich seh schon, dass die Aufgabe ziemlich schwer ist *g*

Ich werd mich eh mehr mit Timer bzw. delay beschäftigen müssen :)

klecksinger
07.08.2005, 19:37
Ein einfacher Timer wäre z.B. auch eine Schleife, die nach dem Drücken des Schalters für eine gewisse Zeit nichts tut; innerhalb dieser Zeit muss der Schalter dann wieder losgelassen werden.
Gruss Klaus

izaseba
07.08.2005, 20:00
Also ich habe das mal so gelöst, das ich beim LOW Pegel des Tasters, also wenn er gedrückt ist eine "Hilfsvariable" also einen bit in xbeliebigem Register gesetzt habe.
der wird natürlich immer bei LOW Pegel abgefragt. also in etwa so:
Prüfe nachTaster Pegel LOW -> ja -> prüfe ob Variable gesetzt -> wenn ja zurück , wenn nein prüfe PIN nach HIGH -> wenn ja schalte nach LOW , sonst HIGH -> setze Variable-> zurück

gelöscht wird die Variable nur bei HIGH Pegel am Taster, also nur dann, wenn der Benutzer die Taste wieder losgelassen hat.

Zugegeben, das setze ich immer in einem Timer rein um dem Prellen entgegenzuwirken,
den hat man aber immer irgendwie als Abfall vorliegen.

Ich hoffe, das es verständlich ist, sonst poste ich hier ein Schnipsel, aber erst lassen wir den toeoe was grübeln

Gruß Sebastian

toeoe
07.08.2005, 21:27
Heut hab ich nimmer den Kopf dazu, werd mich morgen mal ransetzen und wenn ich kurz vorm Verzweifeln bin, sprech ich dich dann nochmal an :)
Danke aber schonmal für deine Mühe

izaseba
07.08.2005, 21:39
Ja machmal,

Ich habe es schon rausgesucht, werde noch entsprechend komentieren, und wenn
Du kurz davor bist nach Bascom überzulaufen melde Dich :lol:

Gruß Sebastian

toeoe
08.08.2005, 18:51
So, hab mich jetzt mal rangesetzt, aber mir fehlt der Befehl, womit ich prüfen kann, was in einem Register drinne steht, hier erstmal soweit mein Code:

.include "m8def.inc"

.def temp = r16

ldi temp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, temp
ldi temp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, temp

ldi temp, 0b00000000
out DDRD, temp ;PortD als Eingang
ldi temp, 0b11111111
out DDRB, temp ;PortB als Ausgang
out PortD, temp ;Pullups von PortD aktivieren

main:
sbis PIND, 2 ;Liegt an D.2 high an? Wenn nicht, dann nächsten Befehl überspringen
rjmp LightON ;Rufe Unterprogramm "LightON" auf

rjmp main ;Endlosschleife

LightON:

ldi r17, 0b00000001
sbi PortB, 2 ;B.2 auf high setzen
ret ;zurück

LightOF:

ldi r17 0b00000000
cbi PortB, 2 ;B.2 auf low setzen
ret ;zurück

Bei den Funktionen "LightON" und "LightOFF" ist ja noch eine Zeile frei (über ldi jeweils), dort wollt ich dann erstmal prüfen, was in r17 drinne steht, aber weiß eben nicht wie.
Wenn ich aufm total falschen Dampfer bin, hilf mir bitte *g* ;)

Gruß
Thomas

izaseba
08.08.2005, 19:19
Hallo,
Um Ports zu Prüfen gibt es
sbic port,bit bzw
sbis port,bit
erster vergleich heißt Spring, wenn bit in port clear
zweiter verglech heißt Spring wenn bit in port set

So das für Ports, für Register gibt es da parallel

sbrc wenn clear
sbrs wenn set

Kennst Du diese (http://www.avr-asm-tutorial.net/avr_de/beginner/index.html)
Seite eigentlich ?
Ist voll der muß für alle, die Assembler lernen wollen (schaue mal unter Befehleliste)

Gruß Sebastian

izaseba
08.08.2005, 19:36
Hallo,
hier ist der Code für den Taster...
der erste Code ist ohne Timer, man kann daran schön erkennen, was Prellen ist.
Ob man die LED eingeschaltet bekommt, ist manchmal eine reine Glücksache O:)



.include "m8def.inc"

.equ sperre = 7 ;Taster sperren
.equ LED = PD7
.equ Taster = PD4
.def tmp = R16
.def status = R17

.org 0x000
rjmp reset

reset:
ldi tmp,HIGH(RAMEND)
out SPH,tmp
ldi tmp,LOW(RAMEND)
out SPL,tmp

sbi DDRD,LED
cbi DDRD,Taster
sbi PORTD,Taster
sbi PORTD,LED


loop:
sbis PIND,Taster
rjmp loop0
cbr status,(1<<sperre)
rjmp loop
loop0:
sbrc status,sperre
rjmp loop
sbis PORTD,LED
rjmp loop1
cbi PORTD,LED
sbr status,(1<<sperre)
rjmp loop
loop1:
sbi PORTD,LED
sbr status,(1<<sperre)
rjmp loop


Sorry, ich war zu faul um hier komentare zu schreiben [-X

Und jetzt nochmal das selbe nur mit Timer 0 der alle 26 mS die Taste abfragt.
Hier kann kann sich die Finger wunddrücken, kein Prellen mehr da O:)
Die 26 mS sind auch nicht kritisch, es könnten auch 100 sein, oder schafst Du das einen Taster 10 mal in der Sekunde zu drücken [-(
Achso, Du mußt natürlich noch Deine Ports und Pins anpassen, ich habe, glaube ich vergessen die in der equs oben eizutragen.



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;
;; Tasterschaltung mit einem Timer ;;
;; Quarz 10 MHz ;;
;;Der Timer hat einen prescaller von 1024 das heißt der hat eine ;;
;; Frequenz von 9,76 kHz. Da ich Ihn noch mit 1 vorlade muß er ;;
;; 254 mal durchlaufen um ein Interrupt auszulösen, das wäre etwa;;
;; 38,5 Hz bzw. 26 ms Achtung, es ist auf 10 Mhz ausgelegt, müßte;;
;; aber auch bei anderen Frequenzen klappen nur die Zeiten sind ;;
;; natürlich anders. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;
.include "m8def.inc"

.equ time = 255 -254 ;Damit wird der Timer vorgeladen
.equ sperre = 7 ;Taster sperren (Hilfsvariable)
.equ LED = PD7 ;kein komentar
.equ Taster = PD4 ;kein komentar
.def tmp = R16 ;mein universallregister
.def status = R17 ;Das ist mein Statusregister, wo später die sperre
;gespeichert wird, man sehe es sind noch 7 Bits
;für andere Zwecke frei

.org 0x000
rjmp reset ;Interruptvektor reset kein komentar

.org OVF0addr
rjmp zeitum ;Interruptvektor für Timer0 Überlauf, hier
;spring mein Programm hin, wenn der Timer überläuft
reset:
;;Stack einrichten
ldi tmp,HIGH(RAMEND)
out SPH,tmp
ldi tmp,LOW(RAMEND)
out SPL,tmp
;;Port D ist bei mir mit LED und mit einem Taster belegt
;;PD7 ist die Diode, PD4 ist der Taster
sbi DDRD,LED ;kein komentar
cbi DDRD,Taster ;kein komentar
sbi PORTD,Taster;Pullup am Taster
sbi PORTD,LED ;Da ich meine LED gegen VCC geschaltet habe
;mache ich sie hier aus

;;Timer Register werden belegt
;;Ich nehme hier den Timer 0
ldi tmp,(1<<CS02) | (1<<CS00) ;prescaller ist 1024
out TCCR0,tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp,(1<<TOIE0);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK,tmp ;Register TIMSK ist dafür zuständig
ldi tmp,time ;Hier wird der Timer vorgeladen und zwar mit 255-254
out TCNT0,tmp ; Er läuft 254 mal durch bevor ein Interrupt auftritt
sei ;mein Lieblingsfehler, na was den Interrupts allgemein zulassen

loop: ;meine Hauptschleife
rjmp loop ;Hier herscht totale langeweile

;;Die Routine wird abgearbeitet wenn ein Interrupt stattgefunden hat

zeitum:
push tmp ;Rette universallregister, hier ziemlich sinnlos
;aber Ordnung muß sein
in tmp,SREG ;rette Statusregister, sehe eine Zeile höher
push tmp ;sehe eine Zeile höher
sbis PIND,Taster ;Überspringe nächste Zeile, wenn PIND HIGH ist
;also nicht gedrückt
rjmp zeitum0 ;sonst springe nach zeitum0
cbr status,(1<<sperre) ;Lösche meine Sperre (Bit 7 im Register status)
rjmp zeitum2 ;Springe nach zeitum2
zeitum0:
sbrc status,sperre ;Überspringe nächste Zeile, wenn Sperre nicht gesetzt ist
rjmp zeitum2 ;Springe nach zeitum2
sbis PORTD,LED ;Überspringe nächste Zeile, wenn LED gesetzt(aus) ist
rjmp zeitum1 ;springe nach zeitum1
cbi PORTD,LED ;Schalte LED ein
sbr status,(1<<sperre) ;setze Sperre(Bit 7 im Register status)
rjmp zeitum2 ;Springe nach zeitum2
zeitum1:
sbi PORTD,LED ;Schalte LED aus
sbr status,(1<<sperre) ;setze Sperre(Bit 7 im Register status)

zeitum2:
ldi tmp,time ;Hier war der Fehler und zwar muß
out TCNT0,tmp ; der Timer neugeladen werden(genauso wie im reset)
pop tmp ;Stelle SREG wieder her
out SREG,tmp ;sehe eine Zeile höher
pop tmp ;Stelle universallregister wieder her
reti ;die Interrupt Routine wird verlassen, und
;es wird weiter im Hauptprogramm gearbeitet


Hier habe ich alles ausreichend kommentiert.....
achso, ich hatte gerade 10 MHz Quarz drauf, es müßte aber mit anderen Frequenzen auch klappen.

Wenn Du noch fragen hast, dann frag.

Sollte jemand weitere Anregungen haben, würde ich mich sehr freuen

Gruß Sebastian

toeoe
08.08.2005, 19:49
Hui, das ist aber nett :)

Erstmal zum oberen Code. Was soll das hier genau bedeuten?


.org 0x000
rjmp reset

Man bräuchte doch nicht wirklich eine Funktion "reset:" oder?
Vor allem, wieso springt der dann eigentlich irgendwann in "loop:" rein? steht ja nirgends oben was von "rjmp loop".

Und was genau ist der Unterschied zwischen equ und def?

Sorry, für die dummen Fragen, aber bin ja neu im Gebiet ;)

Den zweiten Code schau ich mir an, wenn ich den ersten perfekt behersche ;)

Gruß
Thomas

So, nun erstmal was essen :)

izaseba
08.08.2005, 21:03
Sorry, für die dummen Fragen, aber bin ja neu im Gebiet Zwinkern

Es gibt keine dummen Fragen, das kennst Du ja.

Ich lege Dir wirklich den oben genannten Link von mir ans Herz, es ist besser als 1000
Bücher !
Ich glaube , es wäre doch besser, wenn ich den erste Code auch komentiert hätte :-k
Also ich versuche Dir das in ein paar Worten zu erklären.
Ich hoffe, daß es mir gelingt :-b




Erstmal zum oberen Code. Was soll das hier genau bedeuten?
Code: [View More of this Code] [View Even More of this Code] [View Less of this Code] [Select All of this Code]

.org 0x000
rjmp reset

Man bräuchte doch nicht wirklich eine Funktion "reset:" oder?


In diesem einfachem Beispiel hast Du vollkommen recht das muß hier nicht sein.
Aber sobald Du mit Interrupts anfängst, kommst Du nicht drumherum.

.org 0x000
rjmp reset
soll soviel heißen daß in Flashspeicher(da wo das Programm gespeichert ist)
an der 0x000 Stelle den Befehl rjmp reset schreiben soll.
Und nachdem Du Deinen AVR einschaltest, fängt er immer an der adresse 0x000
an zu laufen.
Mithin springt er sofort zum label reset.
Und im reset kommen alle Anfangsfeierligkeiten rein, Register umbenennen, irgendwelche
Werte zuweisen, Timer UART, TWI usw einrichten, und und und.





Vor allem, wieso springt der dann eigentlich irgendwann in "loop:" rein? steht ja nirgends oben was von "rjmp loop".

Der springt garnicht in den loop rein, sondern gleitet ganz sanft darein,
warum auch nicht? nachdem er die letzte Zeile im reset abgearbeitet hat,
geht er einfach in die nächste Zeile über, die halt loop heißt,
loop: ist auch keine Funktion oder sowas, es ist einfach nur ein Label um diese Speicherzelle später besser zu erreichen.

Anmerkung: Du darfst nicht in Kategorien von C oder Bascom denken, es gibt keine
Funktionen der Art wie in Hochsprachen beim Assembler!
Der µC fängt bei der Adresse 0x00 an und geht Speicherzelle für Speicherzelle nach unten,(wenn er kein jmp oder call findet) ich hoffe das ist jetzt klar.


Und was genau ist der Unterschied zwischen equ und def?


das ist einfach erklärt: def brauchst Du um Register unzubenennen, es ist wohl einfacher zu schreiben : meinregister als : R16 zum Beispiel
Da sagst Du dem Assembler : überall wo ich meinregister stehen hab, mein ich eigentlich R16

equ braucht man um irgendwelche Werte zuzuweisen z.B.

.equ LED1 = PD4
Jetzt fragst Du Dich, wo da PD4 ein Wert ist, was?
wenn Du Dir die "m8def.inc" anschaust, findest Du irgendwo folgende Zeile:
.equ PD4 = 4
mithin heißt es auch LED1 = 4

und wozu das ganze ?
Es ist wieder einfacher zu schreiben sbic PORTD,LED1 als sbic 0x12,4 oder ?
zu 0x12 suche in "m8def.inc" nach PORTD dort steht
.equ PORTD = $12 ($12 oder 0x12 bedeutet daß es eine hexzahl ist)
.equ meinezeit = 10000
heißt wiederrum überall wo meinezeitsteht meine ich eigentlich 10000

verstanden ?

So, soviel zu meinem minitutorial für Assembler
Jetzt gehe ich essen
O:)

P.S. Sollte ich irgendwas geschrieben haben, was nicht so ganz stimmt, oder besser ausgedrückt werden könnte, möge mich hier jemand korigieren.

Aber wie gesagt, der link von mir, soll Deine Assemblerbibel werden!

Gruß Sebastian

toeoe
08.08.2005, 21:50
Erstmal nenn großes Dankeschön an dich, dassde dir so viel Mühe mit mir gibst ;)
Die Seite ist echt gut. Ich merk auch schon, dass ich mich mehr mit Interrupts beschäftigen muss, ne? ;)
Das mit PORTD und so, also dass die in der include-Datei definiert sind, weiß ich schon, versteh aber leider nicht wirklich den Sinn, von PD4. Spricht PD4 den 4 Pin von PortD an, oder ist PD4 nur sone Art "integer" mit der Zahl 4?
Nach der Zeile "PORTD, LED1", wobei ja LED1 = PD4 = 4 ist, würde ich auf das zweite tippen. Also das PD4 nur den Wert "4" wiedergibt, oder?

So, nun werd ich den Code mal abtippen (davon lern ich am meisten) und dann veränder ich immer mal wieder was, damit ich seh, was sich verändert :)

izaseba
08.08.2005, 22:16
Du mußt Dir vor den Augen halten, daß ein Port sozusagen ein Byte darstellt
Ein Byte -> 8 Bits bei PortD kann man das ganz gut sehen. Man zählt immer von
rechts nach links und man Fängt immer bei null an:

PortD -> 0 0 0 0 0 0 0 0
in Pin augedrückt PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0
damit ergeben sich 7 6 5 4 3 2 1 0
in zwier Potenz 128 64 32 16 8 4 2 1

mithin heißt ein Ausdruck wie sbi PORTD,PD4
setze am PORTD das Bit 4 mit dem Ergebnis 00010000

Bedenke daß man immer bei null anfangen muß.

Wie Du siehst ist es für Dich einfacher dem assembler zu sagen, daß Du PD4 setzen willst, als erstmal anfangen zu zählen, um welchen bit es sich handelt.

Gruß Sebastian

P.S. Na ja, früher oder später kommst Du um die interrupts nicht rum, aber
glaube mir, sie sind einfacher, als Du denkst.
Hauptsache Du begreifst erstmal, wie der Programmablauf funktioniert und was die
Ganzen Ports und Pins zu sagen haben

toeoe
08.08.2005, 22:45
Jo, das mit den Ports und Pins hab ich denk ich so weit drauf (wenn man das so nennen darf *g*)
Ich lasse grad den ersten Code debuggen und gehe jeden Schritt einzeln durch, klingt mir alles logisch, aber ob ich selbst drauf gekommen wäre?...

Eine Frage aber noch zu der Zeile:
sbr status, (1<<sperre)

Also was die Zeile macht, ist mir klar, in das Register status (r17) wird eine 1 an Bit 7 gesetzte --> 10000000
Aber das "(1<<sperre) ist mir nicht ganz klar. Die 1 wäre hier bei sbr logisch, da man eine 1 setzte, aber bei cbr kommt ja auch die 1 im Befehl vor.
Wie gesagt, die Wirkung der Befehle versteh ich, aber man will ja auch wissen, wieso man das genau so schreiben muss ;)

Gruß
Thomas

izaseba
08.08.2005, 23:12
Ich habe mir schon gedacht, daß da so eine Frage kommt,
da ich jetzt noch eine rauche, bevor ich haja geh kurz eine Antwort.

also dieses Konstrukt 1<<sperre (schaue mal bei .equ sperre = 7) bedeutet nichts
anderes als 1<<7
das heißt schreibe in Register status eine 1 womit der Zustand von status
so aussieht : 00000001
und dieses <<7 heißt und verschiebe es um 7 (.equ sperre = 7) Stellen nach links.
Jetzt ist aus 00000001 10000000 geworden klar ?
sbr sperre,(1<<status) -> sbr sperre,b10000000 oder 128 oder 0x80 je nach Zahlensystem

dieses 1 bedeutet nicht einschalten sondern ein bit
Du hast sicher ein cbr status,(0<<sperre) erwartet, oder ?
Das wäre falsch, weil cbr möchte wissen welchen bit es löschen soll deswegen auch
(1<<sperre) klar ?

Solche Konstrukte wirst Du noch sehr oft sehen, aber man gewöhnt sich daran,
es ist wieder da um Dir das Leben einfacher zu machen, schau, es kann mir im endeffekt wurst sein, ob sperre = 7 oder 5 oder 3 is,t das wird einfach einmal am Anfang festgelegt
und beim Progen brauch ich mir kein Kopf darum machen welche wertigkeit es hat, klar?

Was ich persönlich blöd finde, daß ich bein sbr und cbr den wert k255 setzen muß also
(1<<sperre) aber bei cbi und sbi ein sbi PORTD,sperre also b7 reicht , genauso beim Vergleichen ein b7 also ohne (1<<blabla),
mit der Zeit gewöhnt man sich dran und nimmt es einfach so hin, aber da hatte ich anfangs auch Fehler gemacht.

Am besten druckst Du Dir die Befehlstabelle von www.avr-asm-tutorial.net aus,
dann hast Du alle Befehle immer zur Hand, kannst Sie überall mitnehmen,
ich hab die besten Einfälle aufm Klo 8-[

Ich hoffe, Du bist wieder ein Stück weitergekommen.

Gruß Sebastian

toeoe
08.08.2005, 23:34
Oh ja, nun hab ich das auch verstanden :)

Aber ich glaub, du hast in deinen Kommentaren Fehler drinne.
Beispielsweise hier: sbi PORTD,LED ;Schalte LED aus
Er "setzte" das Bit doch, also auf 1. Da schaltet sich die LED doch dann auch ein.
Denn bei 0b00000001 leuchtet ja auch die LED an Bit 0.
Hoffe, das ist so richtig ;)

So, nun geh ich auch inne Heia

Nacht
Thomas

izaseba
09.08.2005, 15:43
Aber ich glaub, du hast in deinen Kommentaren Fehler drinne.

Ich freue mich, daß Du die Code auch Debuggen tust.
So versteht man auch, was da abgeht.

Ne Du, es ist kein Fehler, Meine Dioden habe ich gegen VCC geschaltet, d.h. wenn Pin Low ist, leuchten sie.
Es wird meistens so verschaltet.

Gruß Sebastia

toeoe
09.08.2005, 16:18
Hmm...kommt nun vlt. ein bißchen doof, aber was ist genau gegen VCC? gegen die normale Schaltrichtung?
Dann sind meine also nicht gegen VCC geschaltet, denn wenn bei mir ne 1 ist (also high) dann leuchtet die LED.

Ggruß
Thomas

Florian
09.08.2005, 16:32
Huuuiiii, hallo Thomas und Sebastian!
Da kommt der Moderator mal wieder zu spät! ;o)
Danke für die Mühe, die Du, Sebastian, Dir gibst!
Ich denke, dass dieser Thread sehr hilfreich für Anfänger ist und deswegen markiere ich ihn mir mal! ;o)

@ Thomas:
Wenn der Sebastian mal nicht da ist, dann kannst Du mich auch gerne ansprechen! *assemblernachwuchs muss gefördert werden ;o)*

izaseba
09.08.2005, 16:46
*assemblernachwuchs muss gefördert werden ;o)*

Deswegen versuche ich dem Thomas zu helfen, Du hast recht,
es macht Spaß, wenn jemand versucht die AVR's richtig zu verstehen O:)

@Thomas,

Der Stromfluß bei meiner Diode ist von dem Pluspol (VCC) über die Diode zum PIN,
deswegen Leuchtet Sie wenn Pin LOW ist.
Wenn das andersrum wäre (also von Pin über Diode zum Minuspol) dann müßte
Der Pin High sein, um die Diode zum leuchten zu bringen.

Wie gesagt, es wird meistens so gemacht.

Gruß Sebastian

Florian
09.08.2005, 16:49
Deswegen versuche ich dem Thomas zu helfen, Du hast recht,
es macht Spaß, wenn jemand versucht die AVR's richtig zu verstehenGenau! ;o)
Danke, dass Du mir unter die Arme greifst, ich habe in den Ferien gerade einiges zu tun und bin etwas weniger online!

toeoe
09.08.2005, 16:49
Ahh, der Moderator höchstpersönlich ;)
Ich finds nett, dass ich hier so freundlich aufgenommen wurde. Besonderen Dank fällt natürlich an Sebastian, der sich besonders um mich bemüht und sicher auch mal die Augen verdrehen muss ;)

Danke auch Florian, dass ich mich an dich wenden darf.
Ich gehe auch grad den Code mit dem Timer durch, also damit das Prellen des Tasters unterdrückt wird.
Aber was ich nicht ganz verstehe. Er führt doch nur einmal am Anfang beim Starten des Programm das hier aus


;;Timer Register werden belegt
;;Ich nehme hier den Timer 0
ldi tmp,(1<<CS02) | (1<<CS00) ;prescaller ist 1024
out TCCR0,tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp,(1<<TOIE0);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK,tmp ;Register TIMSK ist dafür zuständig
ldi tmp,time ;Hier wird der Timer vorgeladen und zwar mit 255-254
out TCNT0,tmp ; Er läuft 254 mal durch bevor ein Interrupt auftritt
sei ;mein Lieblingsfehler, na was den Interrupts allgemein zulassen

Und dann springt er ja zur loop und wartet bis ich den Taster drücke und springt dann in den Interrupt zeitum. Und dort wird nichts mehr von timer oder was anderes erwähnt. Also woher weiß das Programm dann, dass er warten soll?
Wenn ich das richtig verstanden habe, ist das sone Art Standardcode, den man oben beim "reset" einfügt, sodass dann bei jedem Interrupt eine bestimmte Zeit gewartet wird?
Und so wie ich das sehe, sind die Register TCCR0, TIMSK, TCNT0 festgelegte Register für Timer-Sachen?
Und was hat das 255-254 genau zu bedeuten? Ok, er läuft 254 mal eine Schleife ab, bevor ein Interrupt durchgeführt wird, aber wieso dann die 255?
Fragen über Fragen, aber der Mensch ist ja neugierig :)

Gruß
Thomas

[edit]
Ok, danke Sebastion, ich denk ich hab das mit dem VCC soweit verstanden, muss ich dann nochmal genauer schauen, wie das bei mir ist. Solang ich den Code kommentiere, damit ich ihn noch verstehe, ist das denk ich in Ordnung.

Florian
09.08.2005, 16:54
Ahh, der Moderator höchstpersönlich*g*
Lang, lang, lang ist's her ...
da fing ich auch mal so an! ;o)
Ich kenne die Probleme, die man hat und wie verloren man sich ohne jemanden, der einem hilft, fühlt!
Viel Erfolg noch!

izaseba
09.08.2005, 17:10
Ich glaube wirklich, daß es ein Tutorial wird....

OK, mit Interrupts hast Du noch keine Erfahrungen, naja, aber jetzt.


Und dann springt er ja zur loop

Das habe ich schonmal gesagt, er springt nicht sondern, nachden die letzte Zeile in reset abgearbeitet wurde, geht er zur nächsten, die einfach nur loop heiß


und wartet bis ich den Taster drücke und springt dann in den Interrupt zeitum.

Nein so geht es nicht ab.

loop ist ja meine Hauptschleife, dort arbeitet das Programm schön Zeile für Zeile ab.
Den Timer habe ich auf etwa 26 mS eingestellt, das heißt wenn 26 mS um sind,
wird ein Interrupt ausgelöst und dann springt er in zeitum: rein.
Dort wird nachgeschaut, ob Taster Low ist, wenn nicht, verläßt er zeitum: wieder und
geht an die Stelle im Programm zurück, wo er durch den Interrupt unterbrochen wurde.

Das Programm wartet nicht auf die Taste !!!
Es ist ihm egal ob Sich da was tut oder nicht, es macht munter seine Arbeit(naja in meinem Beispiel springt nur zwischen loop und rjmp loop hin und her) es wird einfach nur alle 26mS abgebrochen.


Wenn ich das richtig verstanden habe, ist das sone Art Standardcode, den man oben beim "reset" einfügt

Naja Standard, weiß nicht es sind immer die gleichen Register, die vorbelegt werden müssen.


sodass dann bei jedem Interrupt eine bestimmte Zeit gewartet wird?

Es wird nicht gewartet, jetzt müßtest Du das verstehen(sehe oben)


TCCR0, TIMSK, TCNT0
Ja, davon gibt es noch mehr(der Mega hat 3 Timer), sehe link von mir


Und was hat das 255-254 genau zu bedeuten

Der Timer läuft bei 255 über und verursacht einen Timer Overflow Interrupt(zeitum: sehe
.org OVF0addr
rjmp zeitum) oder DattenBlatt
ich habe mir ausgerechnet, daß er genau 254 mal hochzählen muß, bevor er überläuft,
dann ist es einfacher zu schreiben 255-254)

klaro?

Gruß Sebastian

toeoe
09.08.2005, 17:23
Axo, also alle 26ms (oder was man halt einstellt) ruft er ein Interrupt auf bzw. löst eines aus? Und was passiert, wenn man mehrere Interrupts hat? Ist ja gut möglich bei größeren Programmen. Ruft er dann eines nach dem anderen auf?
Was ich ja echt erstaunlich finde ist, dass 26 ms ausreichen. Ist das Prellen des Tasters von so kurzer Dauer? Also das heißt ich drück den Taster und bevor die 26ms um sind, prellt der Taster gar nicht mehr?

ich habe mir ausgerechnet, daß er genau 254 mal hochzählen muß
Gibt es dafür auch eine Formel? Oder läuft der Timer auf jedem Chip gleich schnell durch? Denn man kennt ja das Problem vom delay, dass dieser bei schnelleren CPUs schneller durchläuft und sich somit eine Figur in einem Spiel dann auch schneller bewegt (wenn man die Move-Funktion mithilfe von delay programmiert).

Gruß
Thomas

izaseba
09.08.2005, 17:27
Nochwas,
Der Timer läuft ganz nebenbei, hat also nichts mit dem Hauptprogramm zu tun,
das einzigste, was er macht, er unterbricht das Hauptprogramm immer, wenn er übergelaufen ist und Springt zu meiner zeitum Funktion bis er auf reti stößt.

Das stimmt auch nicht so ganz, weil der zu Adresse $009 springt , die in m8def.inc mit
.equ OVF0addr=$009 definiert ist.

Und wie er dann den Weg nach zeitum: findet, darfst Du selber rausfinden.

Gruß Sebastian

izaseba
09.08.2005, 17:39
klar ist das mit den Zeiten von Quarz abhängig.

Schau, irgendwo habe ich den Prescaller auf 1024 eingestellt,
suche jetzt im Dattenblatt nach dem Register TCCR0 und schaue Dir mal an was man damit anstellen kann.

Prescaller 1024 heißt, daß der haupttakt(also der Takt vom Quartz) 1024 mal hmm, ich sagmal auftretten muß, bevor mein Timer um eins erhöht wird.

Die zweite einstellsache ist der Wert des Taimers ich habe hier 255-254 also lade ich Ihn mit 1.
alle 1024 Takte wird er um eins erhöht, bis er bei 255 ankommt, und wenn er dann noch einmal +1 macht, dann läuft er über, weil 1 Byte höchstens 255 beinhalten kann 11111111

Dann Macht er erst den Interrupt.
Jetzt mußt Du rechenen mein quartz ist 10 Mhz / 1024 / 254 ergibt etwa 38,5 Hz also 26 mS

Mehrere Interrupts, ja es gibt beim Mega8 14 glaube ich aus dem Kopf.
wenn 2 gleichzeitig auftreten, was eine Glücksache ist aber nicht unmöglich gewinnt derjenige der höher in der liste steht, schaue m8def.inc
ganz unten stehen sie alle der erste ist der wichtigste, und dann geht es ab nach unten.

Dann hat der, der tiefer in der Liste steht verloren und geht leer aus :(

Gruß Sebastian


Nachtrag; ich habe 19 Interrupts gezählt, mei Kopf ist doch nicht mehr der beste....

toeoe
09.08.2005, 17:52
Ahh, das klingt ja schon alles viel verständlicher :)


Und wie er dann den Weg nach zeitum: findet, darfst Du selber rausfinden.
Weil unter "org OVF0addr" das hier steht --> "rjmp zeitum" Also springt er dann zu zeitum :muh: *g*

Aber wie weiß er denn, dass er nach der Zeit zu "OVF0addr" springen muss? Das sehe ich nirgends als Befehl oder so.

izaseba
09.08.2005, 17:59
Weil unter "org OVF0addr" das hier steht --> "rjmp zeitum" Also springt er dann zu zeitum :muh: *

Ich sehe, daß man Dir manche Sachen nicht 2 mal sagen muß, gut.

Das gibt es auch nicht als Befehl, das ist intern und von Atmel festgelegt,
steht in der .inc Datei ganz unten drin
passauf 0x000 -> Reset hier fängt er immer an
0x001 -> External Interrupt0 Vector Address hier springt er immer hin wenn eksterner Interrupt0 ausgelöst wurde
0x002 -> External Interrupt1 Vector Address
....
0x009 ; Overflow0 Interrupt Vector Address -> und hier wenn der Timer 0 übergelaufen ist ?

jetzt kommen wir der Sache mit .org blabla schon näher, was ?

toeoe
09.08.2005, 18:02
Ahhhhh.
Und von diesen Adressen gibt es 9 Stück, wo man dann festlegen kann, wenn er da oder da ankommt, soll er da oder da hinspringen?

Jetzt mal mehr spekulativ:
Wenn ich die ganzen Zeilen mit dem Timer und so löschen würde, aber das

org OVF0addr
rjmp zeitum
lassen würde, dann würde er jeder ms oder ns, also IMMER den Interrupt auslösen?

izaseba
09.08.2005, 18:15
Und von diesen Adressen gibt es 9 Stück

Falsch, schaue dir die m8def.inc an dann kannst Du eine Richtige Aussage machen(die letzten Zeilen in der Datei)


wenn er da oder da ankommt, soll er da oder da hinspringen

nicht ankommt, sondern wenn der und der Interrupt ausgelöst wird.

Und die letzte Frage, vorausgesetzt, Du aktivierst Die Interrupts im Reset, eine Hauptschleife vorhanden ist und zeitum mit einem reti beendet wird,
würde das gehen, ja

So jetzt gahe ich mal einkaufen, (habe es geschaft meine Frau von Enemy Teritorry wegzuziehen #-o )

lese in der zwischenzeit etwas im Datenblatt über die Timer.

Gruß Sebastian

Florian
09.08.2005, 21:01
Ich glaube wirklich, daß es ein Tutorial wird....Hallo Sebastian!
Also wenn Du zu viel Zeit hast, könntest Du ja ... *grins*
Naja, ich habe das "Tutorial" ja markiert, so kann ich es gut wiederfinden und anderen Änfängern besser helfen!

izaseba
09.08.2005, 21:46
Hallo Florian,

Im Moment habe ich etwas Zeit, das Wetter ist ja nicht mehr so toll,
der Urlaub ist noch ein paar Wochen entfernt, Frau schießt einen nach dem
anderem am Nebencomputer ab, und ich selber habe keine große Idee
war ich jetzt großes mit meinem AVR anstellen soll.

Dann werde ich das Frage Antwort Spielchen hier weitermachen, vielleicht hilft es
wirklich noch jemandem.
Ich hoffe aber auf Deine Hilfe, wenn ich nicht weiter weiß, was hoffentlich
nicht so schnell passiert. O:)

Gruß Sebastian

Florian
09.08.2005, 22:03
Hallo Sebastian!
Hast Du es gut, ich habe in zwei Wochen schon wieder Schule! :o(

Ich hoffe aber auf Deine Hilfe, wenn ich nicht weiter weiß, was hoffentlich nicht so schnell passiert.Kein Problem, ich versuche zu helfen, wo ich kann!
Einziger Haken ist TWI, ich stehe seit fast einem ganzen Jahr mit ihm auf Kriegsfuß und bekomme es nicht hin, alle anderen Sachen habe ich mit dem AVR schon zustande bekommen, aber dieser verdammte TWI will nicht! *g*


-> hast Du vielleicht einen funktionierenden M16-Mastertransmitter- (der scheint laut Debugging zu funktionieren) und einen funktionierenden M8-Slavereceiver-Code, der einfach ein Byte rüberschiebt? ;o)
Der Mastercode funktioniert mit einem PCF8574! *stolz sei*

izaseba
09.08.2005, 22:17
Hallo Florian,
Du wirst es nicht glauben, aber ich bin zur Zeit auch mit TWI dran.
Habe gestern die ersten Erfolge mit M8 als Master und einem Tempsensor DS1621.
Es klappt wunderbar, kann die Temperatur auslesen, Thermostat einstellen usw.

Ja es wäre eine Idee einen zweiten Mega als slave noch dadranzuschalten, müßte eigentlich
lösbar sein,
bin aber leider noch nicht dazugekommen es zu versuchen.

Gruß Sebastian

toeoe
09.08.2005, 22:29
So, bin grad vom Kino wiedergekommen (Die Insel) :)



Und von diesen Adressen gibt es 9 Stück
Falsch, schaue dir die m8def.inc an dann kannst Du eine Richtige Aussage machen(die letzten Zeilen in der Datei)
Ich mein natürlich 19 Adressen, haste ja selbst oben geschrieben.



wenn er da oder da ankommt, soll er da oder da hinspringen
nicht ankommt, sondern wenn der und der Interrupt ausgelöst wird.

Axo, ja, ist klar. Also wenn der Timer überläuft, dann löst er den Interrupt aus.

Hab nun erstmal keine weiteren Fragen. Befasse mich weiter mit dem Thema und hoffe, dass ich keine weiteren Fragen mehr hab ;)

Gruß
Thomas

izaseba
09.08.2005, 22:46
und hoffe, dass ich keine weiteren Fragen mehr hab Zwinkern

Das glaube ich nicht (war nicht böse gemeint.)

Ich mach Dir ein Vorschlag,

Schreibe einfach ein kleines Programm, um einfach eine LED in Sekundentakt blinken zu lassen.

Damit was Du bis jetzt weißt, dürfte das nicht das große Problem sein :-({|=
Benutze aber bitte den Timer0 !
achso ich habe einen Fehler in meinem Code gefunden :-&
Habe Ihn korigiert, einfach nur vergessen, den Timer neuzuladen (in der Interrupt Routine)
da steht er ja bei null .

Gruß Sebastian

Florian
09.08.2005, 23:14
Hallo Sebastian!

Du wirst es nicht glauben, aber ich bin zur Zeit auch mit TWI dran.*grins*

Habe gestern die ersten Erfolge mit M8 als Master und einem Tempsensor DS1621.
Es klappt wunderbar, kann die Temperatur auslesen, Thermostat einstellen usw.Das klingt ja gut, ich mag die Porterweiterungen PCF8574 sehr gerne, sind zum Mastererproben sehr zu empfehlen!

Ja es wäre eine Idee einen zweiten Mega als slave noch dadranzuschalten, müßte eigentlich lösbar sein, bin aber leider noch nicht dazugekommen es zu versuchen.Was nicht ist, kann ja noch werden, vielleicht hast Du ja mal ein paar Minuten (= Tage bzw. Wochen) Zeit Dich damit zu beschäftigen? *schleim -> grins*

Gute Nacht, ich gehe jetzt Bubu machen!
VLG
Florian

toeoe
09.08.2005, 23:20
Ok, werd ich dann aber wohl morgen (heute) machen, ist ja auch schon spät.

Nochmal zur Zeit. Wie weiß ich denn, wieviel Mhz ich hab? Also ich glaube, dass mein Teil auf 4 Mhz eingestellt ist, da bei TwinAVR immer 4000 KHz steht. Heißt das, dass das Programm nun 15,4 Hz also ca. 64,9 ms wartet?

Ich muss ja irgendwie auf 1 Hz kommen, damit er im Sekundentakt abfragt, oder?
Entweder es ist schon zu spät, oder ich kann nicht mehr rechnen, ich weiß einfach net, wie ich auf die 1Hz kommen soll. Also was ich anstatt von "255-254" eintragen soll.

izaseba
09.08.2005, 23:43
Nochmal zur Zeit. Wie weiß ich denn, wieviel Mhz ich hab?

Ja das wäre die Voraussetzung um die Aufgabe zu lösen :-k
Was hast Du für ein Board ?
ist da kein Quarz zu sehen? wenn ja müßte es drauf stehen....
vielleicht steht Dein Mega noch auf Internen Takt :-k
Vielleicht kannst Du Deine Fusebits auslesen, aber mit auslesen meine ich auch auslesen und nicht setzen !!!!!!!
Wenn nicht dann gehe von 4 MHz aus.


Ich muss ja irgendwie auf 1 Hz kommen, damit er im Sekundentakt abfragt, oder?

Sehr gut überlegt =D>


Entweder es ist schon zu spät, oder ich kann nicht mehr rechnen, ich weiß einfach net, wie ich auf die 1Hz kommen soll. Also was ich anstatt von "255-254" eintragen soll.

Es ist weder zu spät noch kanst Du nicht rechnen,
die Frage habe ich erwartet, was meinst Du ? Ich wollte Dir die Aufgabe nicht zu leicht machen [-X

Ein kleiner Schupser auf den richtigen Gleis ->
Du kannst nicht mehr langsamer werden mit einem 8 Bit Timer, das hast Du richtig erkannt....
Das muß doch irgendwie anders zu lösen sein :-k
Vielleich einen geraden Teiler einer Sekunde :-k
1/50 vielleicht 1/100 ? 1/200 :-k :-k
Bedenke Du hast noch jedemenge Register frei , vielleicht eine Variable ? Man kann doch Register als Vabiablen Benutzen...... in so ein Register passt ja was zwischen 0 und 255

So schluß, gehe jetzt pennen, lass Dir Ruhig Zeit damit, wenn Du irgandwann die Tage
ein Programm mit der Lösung hast, dann bin ich stolz auf Dich, und auf mich auch, weil ich Dir dann doch was beigebracht habe...
Aber nicht schummeln ! wäre gut wenn Du das selber hinkriegst!!!!!

Gruß Sebastian

toeoe
09.08.2005, 23:50
Hmm..nagut, nun erwartest du ja was von mir :o *zitter* *g*
Ich hoffe, ich bekomm es hin, will dich ja nicht enttäuschen.

Werd mich da noch ein wenig dran setzen, bevor ich schlafen geh.

Gruß
Thomas

Florian
10.08.2005, 12:12
Hallo Thomas!
Es ist wirklich nicht leicht, aber es ist auch für einen Anfänger zu schaffen!
Sebastian hat Dir das so schön erklärt, ich glaube an Dich! *g*
Am Besten nimmst Du Dir einen DIN A4 Zettel zum schmieren und kritzelst ersteinmal drauflos.
Es kann entweder leicht grafisch sein, oder eher Stichworte, Hauptsache Du verstehst, was Du da machst!

Noch ein kleiner Tipp, ich hoffe Sebastian verzeiht mir, auf http://www.avr-asm-tutorial.net/avr_de/beginner/index.html (siehe Seite 1) findest Du alles, was Du brauchst!
Ich finde die Seite sehr gut, zwar für manchen Anfänger etwas unverständlich, aber ich glaube an Dich! *g*
Wenn Du dort Zählroutinen mit Branchbefehlen findest, dann bist Du richtig! ;o)

Viel Erfolg und bleib uns bei den ASM'lern erhalten! *g*

toeoe
10.08.2005, 13:09
Hab schon intensiv drüber nachgedacht, aber die Tipps von Sebastian helfen mir leide nicht. Ich weiß einfach nicht, wie ich da noch ein zusätzliches Register einbauen soll. Denn der Timer zählt ja im Hintergrund hoch und löst das Interrupt aus, wenn er überläuft. Ich muss dem Timer irgendwie sagen, dass er mehrmals hochzählen soll (sodass dann 1 Sekunde vergangen ist) und erst dann das Interrupt auslösen soll. Aber das geht ja nicht.
Obwohl..doch...man kann doch ein Register immer um 1 (dezimal) erhöhen. Ja, das ist es doch (glaub ich) Muss mir nachher mal den Befehl raussuchen. Und wenn er dann bei einem bestimmten Faktor angekommen ist, dann (und nur dann) soll er das Interrupt auslösen. Bzw. das Interrupt soll er ja eh auslösen, aber dann prüft er erst nochmal, ob 1 Sekunde vergangen ist.

@Florian: Die Seite befasst sich ja mehr mit dem Befehl "nop", ich solls aber (laut Anweisung ;) ) mit dem Interrupt Timer0 machen.

Hmm..nagut, ich hoffe ihr habt mein Durcheinander verstanden, wenn nicht, heut abend folgt auf jeden Fall Code (ob er funktioniert, weiß ich jetzt noch nicht *g*)

Gruß
Thomas

[edit]
Kann man im Debug-Modus (also beim Simulieren im AVR-Studio) irgendwie den Timer auch überlauen lassen? Denn ich kann so oft wie ich will F11 drücken, er löst nie den Interrupt aus. Deswegen kann ich auch nicht genau rüfen, wann und wie er meine Befehle ausführt.

Florian
10.08.2005, 13:17
Hallo Thomas!
Du bist schon auf dem richtigen Weg zum Ziel, Du hast verstanden, dass es nicht mehr darum geht den Timer zu verändern, sondern seine Interrupts zu zählen und bei einer Sekunde den interrupt weiterzuleiten!
Bei den nop's stehen aber noch ganz wichtige Befehle, die Branch-befehle!

Ja, ich kenne das Durcheinander, ich hab's verstanden! *g*

izaseba
10.08.2005, 16:32
Obwohl..doch...man kann doch ein Register immer um 1 (dezimal) erhöhen. Ja, das ist es doch (glaub ich) Muss mir nachher mal den Befehl raussuchen

Das ist der wichtigste Punkt!
Mit dieser Aussage hat Du die Aufgabe schon fast gelöst,
Du mußt Die nur überlegen wann der Register um 1 erhöht wird, und wo verglichen wird.

Die Befehle, die Florian meint erkennst Du am br.. z.B brne Springe bei ungleich breq Springe bei gleich usw.

@Florian

Ich finde die Seite sehr gut,
Ich finde die Seite Hammer !
Habe Sie auch schon dem Thomas empfohlen

Gruß Sebastian

Florian
10.08.2005, 16:37
Habe Sie auch schon dem Thomas empfohlenHabe ich schon gesehen! ;o)
Ich habe ihm mit den Branch's ein wenig geholfen, da ich dachte, dass er diese nicht so schnell zuordnen können wird!
Ich hatte auch ganz zu Anfang immer mit diesen ganzen BR's Schwierigkeiten, mittlerweile kenne ich alle wichtigen auswändig! ;o)

izaseba
10.08.2005, 16:59
Hallo Sebastian!

Aber ich denke der Thomas schlägt sich gut durch, oder? O:)Ich denke auch, bisher macht er das sehr gut!
Er lernt schneller als ich bei meinen Anfängen, aber mir hat's ja auch keiner erklärt! ;o)

Es ist sicher nicht so einfach für einen Einsteiger Assembler zu lernen...Ja, das denke ich auch, ich finde es aber sehr mutig von ihm, dass er das wagt! ;o)
Ich finde Assembler auch sehr schön und es macht mir Spass!
Und was man dabei alles über den µC lernt ist auch nicht ganz unbedeutend!


Wenn Du magst kannst Du mir eine PN schicken, damit wir Diesen Thread nicht unnötig damit belasten ?Stimmt, gute Idee! -> siehe PN!

Viele Grüße
Florian

toeoe
10.08.2005, 17:09
So,

nun blinkt es zwar, aber nicht im Sekundentakt. Hier mal der Code:


.include "m8def.inc"

.equ time = 255 -254 ;Damit wird der Timer vorgeladen
.equ sperre = 7
.equ LED = PB2 ;LED an B.2
.def tmp = r16 ;Mein Universallregister
.def status = r17 ;Mein Statusregister, ob LED an oder aus ist
.def zaehler = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp pruefZaehler ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, LED ;B.2 als Ausgang
cbi PORTB, LED ;B.2 auf LOW stellen -> LED aus am Anfang
cbr status, (1<<sperre) ;Statusregister r17.7 = 0 setzen (LED aus)

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) | (1<<CS00) ;prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time ;Hier wird der Timmer vorgelaen und zwar mit 255-254
out TCNT0, tmp ;Er läuft 254 mal durch, bevor ein Interrupt auftritt
sei ;Interrupts zulassen

loop:
rjmp loop ;Immer wieder selbst aufrufen -> Endlosschleife

pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0b01000010 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65

zeitum:
clr zaehler ;Zählregister auf 0 setzen
push tmp ;Rette Universallregister
in tmp, SREG ;Rette Statusregister
push tmp
sbrs status, sperre ;überspringe, wenn r17.7 = 1 ist (LED an?)
rjmp zeitum0 ;zu "zeitum0:" springen
sbrc status, sperre ;überspringe, wenn r17.7 = 0 ist (LED aus?)
rjmp zeitum1 ;zu "zeitum1:" springen

zeitum0:
sbr status, (1<<sperre) ;r17.7 = 1 setzen (LED an)
sbi PORTB, LED ;B.2 = 1 setzen -> LED an
rjmp zeitum2

zeitum1:
cbr status, (1<<sperre) ;r17.7 = 0 setzen (LED aus)
cbi PORTB, LED ;B.2 auf 0 setzen -> LED aus
rjmp zeitum2

zeitum2:
pop tmp ;stelle SREG wieder her
out SREG, tmp
pop tmp ;stelle Universalregister wieder her
reti ;die Interrupt-Routine wird verlassen
;und es wird weiter im Hauptprogramm gearbeitet


Hab auch schon versucht, diese Zeile hier "cpi zaehler, 0b01000010" durch "cpi zaehler, 0b11111111" das hier zu ersetzen. Aber blinkt immer noch so schnell.

Aber eigentlich müsste 65x richtig sein. Denn
4MHz / 1024 / 254 = 65 Hz = ca. 15,4ms
Und bei 1 Sekunde muss ich 65 mal die 15,4ms ablaufen lassen.

Hab eben noch versucht, nach "breq zeitum" ein "reti" einzufügen. Nun blinkt er sehr viel langsamer, aber langsamer als 1 Sekunde. Wenn ich nun anstelle von "cpi zaehler, 0b01000010" das hier eintrage: "cpi zaehler, 0b00010000", dann isses knapp ne Sekunde, die er dann immer blinkt. Aber ist ja bestimmt nicht eine Sekunde, sondern auf gut Glück eben, aber es muss ja auch rechnerisch gehen.

Bin denk ich schonmal aufm sehr guten Wege. Nur der letzte "Feinschliff" fehlt eben noch ;)

Gruß
Thomas

Florian
10.08.2005, 17:20
Hallo Thomas!
Das sind die ersten Sachen, die mir auffiehlen!
Du musst unter das "breq zeitum" in "pruefZaehler" noch ein reti einsetzen, um zum "loop" zurückzukehren und den Interruptflag wieder zu aktivieren!
Wäre das nicht, dann spränge das Programm immer locker entweder über "breq" zu "zeitum", wenn das gleich 65 ist, oder er geht gleich weiter zu "zeitum", da er ja nicht zurückgeschickt wird!

Außerdem hast Du noch einen logischen Fehler im Programm, dazu komme ich aber später! ;o)

toeoe
10.08.2005, 17:22
Jo, hab ich ja dann noch gemacht (habs auch oben geschrieben ;) ), aber dann braucht er wesentlich länger als eine 1 Sekunde.

Florian
10.08.2005, 17:23
Außerdem hast Du noch einen logischen Fehler im Programm, dazu komme ich aber später! ;o)Hast Dir im Datenblatt mal die letzten Seiten angeschaut, wo die ganzen Befehle aufgelistet sind?

toeoe
10.08.2005, 17:29
Habs grad offen.
Kann ja eigentlich nur an den 3 Zeilen hier liegen, oder?

inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0b01000010 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65

izaseba
10.08.2005, 17:29
Hallo Thomas,

Du hast schon kappiert, wie es zu lösen ist, gut,
was Dir nur fehlt ist die Erfahrung, aber mit so kleinen Programmen bekommst Du sie schon,

Ich habe mir den Code zwar nicht ganz angeguckt aber schaue hier:


inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0b01000010 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65

zeitum:

Die ersten 3 Zeilen sind geil, so muß es sein, ABER breq zeitum
zeitum:

Er geht IMMER nach zeit um breq Zeitum -> springe nach zeitum , wenn gleich, sonst mach weiter im Programm, und was steht genau unter breq zeitum ??

Verstehst Du ?

Edit. O weh, was bin ich langsam, Gut Florian dann erklär Du Ihm das sonst bekommen wir hier ein durcheinander :-)
noch besser, Du löscht mein Post hier
Ich schaue mir lieber die PN an

toeoe
10.08.2005, 17:31
Jo, ok, das ist klar, war dumm von mir ;)
Aber wie schon oben geschrieben, hab ich nach dem breq ein "reti" reingemacht, aber dann binkt er sehr viel langsamer als eine Sekunde.

Florian
10.08.2005, 17:34
Aber wie schon oben geschrieben, hab ich nach dem breq ein "reti" reingemacht, aber dann binkt er sehr viel langsamer als eine Sekunde.Das ist ein kleiner Denkfehler!
Hast Du Dir die Befehlsliste (Instruction Set Summary) im Datenblatt mal angesehen?
Da steht was von Clocks!
Was wird das wohl bedeuten? ;o)

izaseba
10.08.2005, 17:38
Thomas,
hast Du den Code inzwischen geändert?

Ich meine Die erste version hat mir besser gefallen

toeoe
10.08.2005, 17:42
Ahhh...da muss man aber viel beachten ;)
Das heißt dann wohl, dass die Befehle inc und cpi 1 Takt brauchen und breq nen halben Takt und reti 4?
Das heißt ich muss anstelle von 65...*rechne*hmm...auf jeden Fall sehr viel weniger eintragen muss *g* irgendwie muss ich 6,5 Takte abziehen. Aber 0,5 ist ja schlecht zum abziehen...Entspricht 1 Takt = 1 ms? Dann müsste ich anstelle von 65 die Zahl 46 eintragen...
Hmm..nee, immer noch zu hoch. Wieviel Zeit beansprucht denn ein Takt?

@Sebastian:
Welche 1 Version? Ich hab noch das reti hinzugefügt, mehr nicht.

izaseba
10.08.2005, 17:45
Thomas,
Vergiss Die sache mit 1 Takt hier ein Takt da, quatsch, so Genau kommt es sich nicht!!!



Wieviel Zeit beansprucht denn ein Takt

dann nehme einen taschenrechner und rechne , bei 4Mhz
1 / 4000000 da mach der eine oder andere Takt nichts aus

Florian
10.08.2005, 17:46
Du hast einen 4 Mhz Quarz!
Das sind bei 1 MIPS 4000000 Takte!
Logisch oder? ;o)
1 / 4000000 Sekunde sind 0,000.000.25 bzw. 0,25 µS, wenn ich mich nicht irre!?

Überigens gibt es keine halben Takte, das 1 / 2 soll bloß 1 oder 2 Takte bedeuten!
Ob nun 1 oder 2 Takte hängt von der Sprungrichtung ab!

toeoe
10.08.2005, 17:47
Ok, ist wirklich nicht viel. Aber wie bekomme ich denn raus, wie oft ich die 15,4 ms wiederholen muss, damit 1 Sekunde raus kommt. 65*15,4 = 1001. Wären also 1 Sekunde, aber das stimmt ja nicht.

[edit]
0,25 µs ;)

izaseba
10.08.2005, 17:49
Aber wie gesagt langsam, keiner will hier schnell was machen,
Der Anfang wa echt super, lass Dich mit sowas nicht durcheinander bringen.

Denke mal mit ->Interrupt-> zaehler +1 -> hab ich 65? -> nein -> lade timer neu -> raus aus den interrupt/ ->ja -> schaue nach ob led an/aus -> schalte sie um -> leere zaehler -> lade timer neu -> verlasse Interrupt

Florian
10.08.2005, 17:51
Hallo Sebastian!
Sehr anschauliche Erklährung, sehr schön!

toeoe
10.08.2005, 17:52
Hmm...ich find das logisch. Seh da leider nirgends nen Logikfehler :/

izaseba
10.08.2005, 17:54
Thomas, in meiner Erklärung oben habe ich was fett markiert......

Florian
10.08.2005, 17:56
So, ich muss dann mal gehn, zu na Party! ;o)
Ich komme morgen dann wieder und schaue mir eure Fortschritte an! *g*

Tschüss und viel Erfolg weiterhin!

toeoe
10.08.2005, 17:58
Ahh, haste geändert. Nun gut. Also den Timer lädt ja immer neu, wenn der Befehl "reti" kommt? Oder? Und er muss ja den Timer neuladen, wenn er das Interrupt auslöst, damit er dann wieder von 0 zählt.
Versteh leider nicht, wo der Fehler ist :(

izaseba
10.08.2005, 18:06
Thomas,
Was ist Dein aktueller Code, das was oben steht?
Wenn nicht, dann Poste mal das was Du jetzt hast, sonst reden wir aneinander vorbei.

Gruß Sebastian

toeoe
10.08.2005, 18:08
Das hier ist mein aktueller Code:

.include "m8def.inc"

.equ time = 255 -254 ;Damit wird der Timer vorgeladen
.equ sperre = 7
.equ LED = PB2 ;LED an B.2
.def tmp = r16 ;Mein Universallregister
.def status = r17 ;Mein Statusregister, ob LED an oder aus ist
.def zaehler = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp pruefZaehler ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, LED ;B.2 als Ausgang
cbi PORTB, LED ;B.2 auf LOW stellen -> LED aus am Anfang
cbr status, (1<<sperre) ;Statusregister r17.7 = 0 setzen (LED aus)

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) | (1<<CS00) ;prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time ;Hier wird der Timmer vorgelaen und zwar mit 255-254
out TCNT0, tmp ;Er läuft 254 mal durch, bevor ein Interrupt auftritt
sei ;Interrupts zulassen

loop:
rjmp loop ;Immer wieder selbst aufrufen -> Endlosschleife

pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0b01000001 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65
reti

zeitum:
clr zaehler ;Zählregister auf 0 setzen
push tmp ;Rette Universallregister
in tmp, SREG ;Rette Statusregister
push tmp
sbrs status, sperre ;überspringe, wenn r17.7 = 1 ist (LED an?)
rjmp zeitum0 ;zu "zeitum0:" springen
sbrc status, sperre ;überspringe, wenn r17.7 = 0 ist (LED aus?)
rjmp zeitum1 ;zu "zeitum1:" springen

zeitum0:
sbr status, (1<<sperre) ;r17.7 = 1 setzen (LED an)
sbi PORTB, LED ;B.2 = 1 setzen -> LED an
rjmp zeitum2

zeitum1:
cbr status, (1<<sperre) ;r17.7 = 0 setzen (LED aus)
cbi PORTB, LED ;B.2 auf 0 setzen -> LED aus
rjmp zeitum2

zeitum2:
pop tmp ;stelle SREG wieder her
out SREG, tmp
pop tmp ;stelle Universalregister wieder her
reti ;die Interrupt-Routine wird verlassen
;und es wird weiter im Hauptprogramm gearbeitet

Hab nur das "reti" vorhin eingefügt.

izaseba
10.08.2005, 18:26
Der Fehler ist ganz einfach,
Du mußt den Timer im Interrupt neu laden, sonst fängt er
immer bei null an und nicht bei time also kurz vor reti den Timer
neu laden, es ist auch schlecht, daß wir 2 retis hier haben, so ist es sehr unsauber,
aber dazu kommen wir später, jetzt wollen wir eine sekunde haben!
Du bist Dir 100% sicher daß Du 4 MHz quarz hast?

Gruß Sebastian

P.S.
Die Aufgabe ist eigentlich schon gelöst weil unser Ziel ja war daß Du
mit Interrupts und Timer arbeitest und das hast Du ja schon gelöst.

toeoe
10.08.2005, 18:41
Ich bin mir nicht ganz sicher, dass ich 4Mhz hab. Aufm Board find ich auch nirgends ne Angabe dazu. Wie sieht son Quarz eigentlich aus? *schäm* Hab das Board hier: http://www.myavr.de/shop/artikel.php?artID=4

Hier der Code, den ich nun geändert hab, geht aber immer noch net, oder lad ich den Timer falsch neu?


pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0b01000001 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65
ldi tmp, time
out TCNT0, tmp
reti

[edit] In der Beschreibung vom Board steht:
Standardquarz: 3,686411-HC49U-S

izaseba
10.08.2005, 18:55
Passauf,


4MHz / 1024 / 254 = 65 Hz = ca. 15,4ms

Da liegt der Hund begraben.....

Ich weiß zwar nicht wie Du darauf kommst, aber es soll heißen

4MHz / 1024 / 254 =15,4 Hz = 65 ms :-b

Mithin muß zeahler 15 mal durchlaufen, um etwa eine sekunde zu bekommen.

Ich habe so gerechnet:

1/4000000 = 250 ns -> dauer eines Taktes
bei 1024 Prescaler 250 nS * 1024 = 256 µs

also ein Timerschritt dauert 256 µS

Jetzt heißt es Anzahl der Schritte zu finden, damit man einen schönen Teiler der Sekunde hat.
Ich habe 98 genommen 256µ * 98 = 25,088 ms

Wenn Du das jetzt mit 40 malnimmst bist Du auch bei einer Sekunde.
Also Doch Timing Problem entweder änderst Du in dem jetzigem Code Deine 65 ? in 15 um, oder lädst den Timer mit 98 und zaehler mit 40.
Wie Du siehst gibt es mehrere Wege eine Sekund zu erzeugen....

Ich hoffe, daß es jetzt bei Dir klappt.

P.S. Quarz auf diesem Board ist das kleine Dingen aus Metall unter dem Mega,
auf dem Bild Rechts davon.

Gruß Sebastian

izaseba
10.08.2005, 18:58
Ja den Timer hast Du richtig geladen, aber bedenke Du hast (noch) 2 retis in Deinem Code drin!!!

toeoe
10.08.2005, 19:10
Ok, auf dem Quarz steht auch die 3,6864, also 4 Mhz dann. Hab die 65 nun in 15 umgewandelt. Muss mich nur nochmal damit auseinandersetzen, wann ich mit 1000 multipliziere und wann ich durch 1000 teile und so.
Aber genau 1 Sekunde blinkt er nicht, ist ein wenig länger, glaube aber nicht, dass es an den 3ms liegt, denn bei 25,088 * 40 kommt ja 1003,52 raus.

Und wieso muss ich vor dem "reti" den Timer neu laden, er springt doch eh zurück zum reset nach dem reti, oder?

Gruß
Thomas

[edit]
Ja, aber das zweite reti bei "zeitum2" brauch ich ja eigentlich auch :o

izaseba
10.08.2005, 19:30
Ok, auf dem Quarz steht auch die 3,6864 , also 4 Mhz dann

das ist nicht ganz richtig, es sind 313600 Takte weniger, dazu kommen ja noch die anderen
Ungenauigkeiten usw.


Und wieso muss ich vor dem "reti" den Timer neu laden, er springt doch eh zurück zum reset nach dem reti, oder?


Falsch, warum soll er nach reset zurück?
dann würde das Programm immerwieder neu Anfangen, oder?

Die Sache mit dem reti ist die, wenn ein Interrupt ausgelöst wird,
merkt sich der µC SOFORT wo er sich gerade im Hauptprogramm (hier loop)
befindet, also in welcher Zeile er gerade arbeitet(wie er sich das merkt, kann ich Dir mal später irgendwann sagen, im moment reicht es wenn Du das nur weißt), die Zeile macht er noch zu ende,
und geht in die Interruptroutine rein.
In diesem Moment werden auch Interrupts allgemein abgeschaltet, damit nichts zwischenfunken kann(das macht der µC aber von alleine, haben wir nichts mit am Hut)
So wo sind wir jetzt, achso in der Interruptroutine, naja das weißt ja schon,
er geht Zeile für Zeile runter BIS er reti findet.
reti kann man ganz einfach so beschreiben: -> Hey µC du hast Dir mal gemerkt wo ich(Interrupt) Dich unterbrochen habe (irgendwo im Hauptprogramm),
so ich bin jetzt fertig(Interrupt) dann GEHE sofort an die Stelle zurück, wo ich(Interrupt)
Dich (µC) unterbrochen habe, mach dort (also nicht genau dort sondern eine Zeile tiefer)
weiter UND schalte die Interrupts wieder ein!

Ich hoffe, daß man mich hier nicht auslacht, für Diese schreibweise, aber ich muß beruflich auch die heutige Jugend ausbilden, und so kappieren sie das am schnellsten!

Gruß Sebastian

toeoe
10.08.2005, 19:35
Hehe, jo, klingt einleuchtend :)
also springt er genau da hin (eine Zeile tiefer (logisch, denn er soll ja den nächsten Befehl ausführen)), wo er vor dem Auslösen des Interrupts herkam? Wunderbar :)

Nun noch das Problem, warum zwei retis unsauber sind? Ich überleg schon die ganze Zeit, ob ich das reti bei zeitum2 wegnehmen kann, aber ich glaub nicht, denn er soll ja wieder zurückspringen und das geht ja nur mit reti...

Gruß
Thomas

izaseba
10.08.2005, 19:56
und so kappieren sie das am schnellsten!


O:) was habi ich gesagt?
Vollkommen richtig, was Du da sagst.
also muß er einfach nur in rjmp loop reinspringen, weil wir garkein Hauptprogramm haben,
nur die eine Zeile

Jetzt zu den zwei retis.
Du hast Dein Blinkprogramm auf der Grundlage von meinem Taster/Entprellprogramm aufgebaut, ist auch nicht schlimm, nur auf einmal mußtest Du doch was abändern, und dadurch wurde es unsauber.
Das Programm von mir enthält noch Sachen, die Du noch nicht verstehst, und ich habe keine Lust jetzt Dich durcheinanderzubringen, wo Du langsam anfängst kein Beginner mehr zu sein !!!

Ich entferne mal sachen aus der Interruptroutine, die JETZT keinen Sinn ergeben,
und dann kannst Du mal schauen, wie Du mit nur einem reti auskommen kannst.



pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0x28 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65
ldi tmp, time ;Hier wird der Timmer vorgelaen und zwar mit 255-254
out TCNT0, tmp
reti

zeitum:
clr zaehler ;Zählregister auf 0 setzen
sbrs status, sperre ;überspringe, wenn r17.7 = 1 ist (LED an?)
rjmp zeitum0 ;zu "zeitum0:" springen
sbrc status, sperre ;überspringe, wenn r17.7 = 0 ist (LED aus?)
rjmp zeitum1 ;zu "zeitum1:" springen

zeitum0:
sbr status, (1<<sperre) ;r17.7 = 1 setzen (LED an)
sbi PORTD, LED ;B.2 = 1 setzen -> LED an
rjmp zeitum2

zeitum1:
cbr status, (1<<sperre) ;r17.7 = 0 setzen (LED aus)
cbi PORTD, LED ;B.2 auf 0 setzen -> LED aus
rjmp zeitum2

zeitum2:
reti ;die Interrupt-Routine wird verlassen


So, frag aber bitte noch nicht, warum ich dort etwas weggemacht habe,
dabei kommen wir noch aus, irgendwann ......
Schaue Dir diese Code an überlege, lass Dir Zeit, und schicke Das Programm zurück,
wo nur ein reti ganz unten im code steht, vor dem Du dann den zaehler neu lädst.

Müßte jetzt gaaaaanz einfach sein, was kannst Du sonst noch schreiben an der stelle, wo das erste reti steht.......
:-({|=

Gruß Sebastian

toeoe
10.08.2005, 20:11
pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0x28 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65
rjmp zeitum2

zeitum:
clr zaehler ;Zählregister auf 0 setzen
sbrs status, sperre ;überspringe, wenn r17.7 = 1 ist (LED an?)
rjmp zeitum0 ;zu "zeitum0:" springen
sbrc status, sperre ;überspringe, wenn r17.7 = 0 ist (LED aus?)
rjmp zeitum1 ;zu "zeitum1:" springen

zeitum0:
sbr status, (1<<sperre) ;r17.7 = 1 setzen (LED an)
sbi PORTD, LED ;B.2 = 1 setzen -> LED an
rjmp zeitum2

zeitum1:
cbr status, (1<<sperre) ;r17.7 = 0 setzen (LED aus)
cbi PORTD, LED ;B.2 auf 0 setzen -> LED aus
rjmp zeitum2

zeitum2:
ldi tmp, time
out TCNT0, tmp
reti ;die Interrupt-Routine wird verlassen


Funzt :)

[edit]
Kann mir schon vorstellen, warum du das Interruptzeug da rausgenommen hast. Denn mit dem ganzen Zeug, also das Sichern der Register blinkt die LED nimmer so wie sie soll. Logisch, denn tmp hat am Ende den falschen Wert. Aber darauf wollten wir ja später kommen ;)

izaseba
10.08.2005, 20:47
Ich bin begeistert, das ist echt eine sehr schöne Interrupt Routine, und Du sagtest, es geht nicht mit einem reti :-s


Denn mit dem ganzen Zeug, also das Sichern der Register blinkt die LED nimmer so wie sie soll. Logisch, denn tmp hat am Ende den falschen Wert. Aber darauf wollten wir ja später kommen

Ich sehe, daß Du wirklich alles auf einmal wissen willst, aber wie gesagt, das Thema machen wir irgendwann, jetzt nicht, ich sag nur dazu, das es nicht stimmt, was Du da sagst.

Aber wenn Du denkst, daß wir Dein Code jetzt wirklich 100% haben, dann irst Du gewaltig,
es klappt zwar, man könnte aber noch sehr viel verbessern \:D/

Also die nächste Aufgabe (Ich hoffe Du hast Lust Dazu)
Du benutzt den status Register mit der sperre, um Dir zu merken ob die LED an oder aus ist.
Damit hast Du uns schön gezeigt, wie man Register als Variablen nutzen kann,
Man braucht keine variablen an Funktionen zu übergeben, oder irgendeinen Wert zurückzuerwarten wie bei C, sondern hat sie alle sozusagen Global zu verfügung, sie sind überall gültig in Hauptprogramm und in den Interruptroutinen.

So, aber......
Wir wollen Sparen!
Stell Dir vor, Du hast wirklich keinen Register mehr frei, Sram ist voll, und mußt das Programm so umschreiben, daß Du ohne sperre auskommst ! :lol:

Warum schreib ich das jetzt?
Weil Du Dir die Befehlsliste nicht genau angeschaut hast [-X

Und alle Register auch noch nicht kennst.

es gibt da Register, die PIN heißen, für jeden Port einen also bei Mega8 PINB PINC PIND
Da Du Deine LED an PORTB dranhast, ist Der Register PINB schon Deine Variable, wo der Zustand der LED gespeichert ist :-&

Wenn Du jetzt den Befehl in tmp,PINB schreibst wird der Zustand deines Portes B in tmp geschrieben, aber noch nichmals das brauchst Du,
es gibt Befehle wie sbic PINB,LED und sbis PINB,LED ,
und was meinst Du was die machen ?

Versuch das Programm jetzt so abzuändern, daß Du mit hilfe einer der Befehle sbic und sbis auskommst.

Dürfte nicht so schwer sein, denke ich, aber dadurch wird Dein Programm noch schmaler, und eleganter!

Gruß Sebastian

toeoe
10.08.2005, 21:00
YEAH, ich habs gewusst. Hab schon zu meiner Freundin gesagt, dass ich noch was unnötiges drinne habe, aber so hab ich eben gelernt mit Registern umzugehen ;)
Also ist nicht schwer die Aufgabe, hier der Code:

.include "m8def.inc"

.equ time = 255 -254 ;Damit wird der Timer vorgeladen
.equ LED = PB2 ;LED an B.2
.def tmp = r16 ;Mein Universallregister
.def zaehler = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp pruefZaehler ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, LED ;B.2 als Ausgang
cbi PORTB, LED ;B.2 auf LOW stellen -> LED aus am Anfang

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) | (1<<CS00) ;prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time ;Hier wird der Timmer vorgelaen und zwar mit 255-254
out TCNT0, tmp ;Er läuft 254 mal durch, bevor ein Interrupt auftritt
sei ;Interrupts zulassen

loop:
rjmp loop ;Immer wieder selbst aufrufen -> Endlosschleife

pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
cpi zaehler, 0b00001111 ;Wenn Zählregister = 65 ist
breq zeitum ;spring zu "zeitum:" wenn zaehler = 65

rjmp zeitum2 ;zu "zeitum2:" springen

zeitum:
clr zaehler ;Zählregister auf 0 setzen
sbis PINB, LED ;überspringe, wenn B.2 = 1 ist (LED an?)
rjmp zeitum0 ;zu "zeitum0:" springen
rjmp zeitum1 ;zu "zeitum1:" springen

zeitum0:
sbi PORTB, LED ;B.2 = 1 setzen -> LED an
rjmp zeitum2

zeitum1:
cbi PORTB, LED ;B.2 auf 0 setzen -> LED aus
rjmp zeitum2

zeitum2:
ldi tmp, time ;Hier wird der Timer vorgelaen und zwar mit 255-254
out TCNT0, tmp ;Er läuft 254 mal durch, bevor ein Interrupt auftritt
reti ;die Interrupt-Routine wird verlassen
;und es wird weiter im Hauptprogramm gearbeitet

Ich finds echt nett von dir, dass du dir so viel Mühe mit mir gibst. Darf ich mal so nebenbei fragen, was du beruflich machst? Ich tipp mal auf Lehrer?

Gruß
Thomas

PS: Ich glaub, Assembler wird noch richtig lustig ;) :lol:
Mir wird nur schlecht, wenn ich seh wie man Timer in C oder Basic programmiert, wie wenig Zeilen das da sind *g*

[edit]
Hab noch ein Befehl rausgenommen :) Wieder eine Zeile weniger.

SprinterSB
10.08.2005, 21:16
Mir wird nur schlecht, wenn ich seh wie man Timer in C oder BASIC programmiert, wie wenig Zeilen das da sind *g*

Da kann ich dich beruhigen. In C hast du auch eine Zeile für jeden Zugriff auf ein Register. Und wenn es hart auf hart kommt, schreibt man da auch Codepassagen oder genze Module in Assembler.

Und in BASIC weiß man nie, was sich eigentlich hinter dieses öminösen config-Kommandos verbirgt...

toeoe
10.08.2005, 21:20
Da kann ich dich beruhigen. In C hast du auch eine Zeile für jeden Zugriff auf ein Register. Und wenn es hart auf hart kommt, schreibt man da auch Codepassagen oder genze Module in Assembler.
Na denn bin ich ja echt beruhigt.


Und in BASIC weiß man nie, was sich eigentlich hinter dieses öminösen config-Kommandos verbirgt...
Das ist dann allerdings der Nachteil. Irgendwann muss man sicher in die tieferen Ebenen gehen. Naja, ich denk eh, dass ich bei Assembler bleib ;)

izaseba
10.08.2005, 21:25
O man ich dachte, Du wirst Damit etwas mehr zu tu haben :(

Das heißt aber nur daß die Materie doch nicht so schwer ist, wie man es am Anfang
meint, und daß Du gut logisch denken kannst!

Aber ich wäre nicht ich, wenn ich nicht doch was hätte 8-[



zeitum:
clr zaehler ;Zählregister auf 0 setzen
********************sbis PINB, LED
********************rjmp zeitum0 ;zu "zeitum0:" springen
sbic PINB, LED
rjmp zeitum1 ;zu "zeitum1:" springen

zeitum0:
sbi PORTB, LED ;B.2 = 1 setzen -> LED an
rjmp zeitum2

zeitum1:
cbi PORTB, LED ;B.2 auf 0 setzen -> LED aus
rjmp zeitum2

zeitum2:
Es handelt sich um die 2 Zeilen, die ich mit Sternchen markiert habe,
Die brauchst Du nicht!!!!!!!!!!!!
Und warum?
sbic PINB, LED -> überspringe nächste Zeile wenn LED Clear
wenn Clear dann macht er bei zeitum0 weiter sonst macht er rjmp zeitum1
Da LED ein Bit ist also 1 oder 0 brauchen wir das nur einmal zu vergleichen.
Entweder das eine oder das andere.

Ich hoffe, das es klar ist...



Mir wird nur schlecht, wenn ich seh wie man Timer in C oder Basic programmiert, wie wenig Zeilen das da sind *g*


Na ja Basic auf jedem Fall, da steuerst Du ein servo mit einer Zeile an.
und C , hmmm bei C werden die Register genauso geladen!
anstatt
ldi tmp, (1<<CS02) | (1<<CS00)
out TCCR0, tmp

schreibt man TCCR0 = (1<<CS02) | (1<<CS00);

Du kannst immernoch aussteigen :lol:

Und die Sache mit dem Lehrer, nööööö ich arbeite in einer Elektrofirma, wo jedemenge
Praktikanten und Azubis kommen, man schick sie meistens zu mir,
und die können fragen,
da bist Du noch ruhig gegen O:)


aber so hab ich eben gelernt mit Registern umzugehen

naja, sollte nur ein Beispiel sein ....

Gruß Sebastian

toeoe
10.08.2005, 21:41
Ahhja, weil er ja einfach Zeile für Zeile (Adresse für Adresse) abarbeitet. Das muss noch rein in mein kleines Köpfchen ;)

Muss aber wieder kurz auf den Timer zurückkommen. Auf Seite 4 hast du ja geschrieben

entweder änderst Du in dem jetzigem Code Deine 65 ? in 15 um, oder lädst den Timer mit 98 und zaehler mit 40.
Das hab ich ja alles nachvollzogen. Hab das auch auf meine 3,6864 MHz angepasst und den zaehler auf 14 geändert. Klappt dann wunderbar. Nur ein paar ms weichen ab. Dann wollt ich nun die kompliziertere Art machen, also hier erstmal die Rechnung:
1 / 3686400 = 271ns
271ns * 1024 = 278µs
278µs * 90 = 25ms
25ms * 40 = 1s

Diese Ergebnisse nun auf deine Aussage angewendet:

...oder lädst den Timer mit 90 und zaehler mit 40.

Also folgende Änderungen im Code:

.equ time 98-97
und

cpi zaehler, 0b00101000

Dann ist er aber wieder sehr viel langsamer als eine Sekunde. Wäre nett, wenn du mir da auf die Sprünge helfen würdest :)

izaseba
10.08.2005, 21:42
Zu Deinem Edit oben, schaue meine Post von gerade, dann bekommst Du 2 Zeilen weg \:D/

toeoe
10.08.2005, 21:46
Jo, hab ich gesehen und geändert :)

izaseba
10.08.2005, 21:49
.equ time 98-97

das ist wohl hier falsch.......

Mit wieviel willst Du ihn vorladen? mit 90 ?

bedenke Der Zähler läuft von dem Wert was Du vorlädst bis 255 dann kommt der Überlauf.

mithin

.equ time = 255 - 90

255 bleibt immer gleich weil das ja die Schwelle ist wo er überläuft.....

und der zähler dann mit 40 das ist richtig.
Wenn Deine Rechnung stimmt , hast Du dann eine genaue Sekunde......

Jetzt weißt Du warum es so krumme Quarze gibt.....

Gruß Sebastian

toeoe
10.08.2005, 21:56
Ahh, klar :Haue
Ich denk, ich hab den Code nun supi verstanden :)
So, dann hätten wir das jetzt wohl abgehakt?

SprinterSB
10.08.2005, 22:00
bedenke Der Zähler läuft von dem Wert was Du vorlädst bis 255 dann kommt der Überlauf.

mithin

.equ time = 255 - 90

255 bleibt immer gleich weil das ja die Schwelle ist wo er überläuft.....

Gruß Sebastian

Sicher?
Der Zähler Interrupt kommt doch erst in dem Moment, wenn der Zähler überläuft. Das Interrupt-Flag ist ja so was wie ein neuntes Bit, das allerdings nur gesetzt wird.
Da der Interrupt ausgelöst wird, wenn der Zähler auf 0 springt, muss es doch heissen.

.equ timer = 256 - 90

Gruß, Georg-Johann

izaseba
10.08.2005, 22:11
So, dann hätten wir das jetzt wohl abgehakt?

nö,
eine Kleinigkeit hätte ich noch :^o

Pass auf, ich mach eine Aussage, die verdammt wichtig ist, in unserem kleinem Beispiel
ist es vielleicht egal, aber um zu üben gut geeignet.

So jetzt die Aussage :

Interruptroutinen sollen so kurz wie möglich gehalten werden!

Ende de Aussage.

Warum das so ist, müßtest Du schon wissen.
Ich habe das schon irgendwo oben gesagt.
1. Überlege warum das so ist, wie gesagt ich habe es schon oben geschrieben!
2. Versuche Dein Code so abzuändern, daß der Interrupt nichts anderes macht, als Deinen Zähler um eins hochzuzählen, und den Timer neu zu laden!
Der rest soll in Hauptprogramm ablaufen.

es ist denke ich auch nicht schwer, aber die Frage 1 , da bin ich auf Deine Antwort gespannt.

Wenn Du das hast, dann haben wir wirklich alles, und könnten wir uns morgen oder so den Timer 1 anschauen, der hat ein paar Möglichkeiten mehr als der erste, aber bevor Du
damit anfängst mußt Du noch die 16 Bit Zahlen kennenlernen, die sind auch nicht ohne.

Alles aber nur wenn Du noch lust hast.

izaseba
10.08.2005, 22:14
Da der Interrupt ausgelöst wird, wenn der Zähler auf 0 springt, muss es doch heissen.

.equ timer = 256 - 90


Gut das Du das ansprichst, sorry, es muß wirklich so heißen,
somit haben wir ein Takt mehr, also wieder ein paar µs gewonnen

Gruß Sebastian

toeoe
10.08.2005, 22:32
Hmm...die Aufgabe ist schon ziemlich schwer. Also die Interruptroutine ist doch eigentlich indirekt auch zeitum, zeitum1 und zeitum2, oder? Denn dort springt er ja in preufZaehler hin. Oder brauch ich zeitum, zeitum1 und zeitum2 gar nicht anrühren und nur pruefZaehler und die loop ändern?

Gruß
Thomas

izaseba
10.08.2005, 22:39
alles was zwischen preufZaehler und reti steht, wird in dem interrupt abgearbeitet ja, nachdem Du Fertig bist soll dazwischen nur inc zahler und Timer neuladen stehen der rest im Hauptprogramm, komm das schafst Du.

Ich geh jetzt schlafen, wie gesagt lass Dir ruhig zeit damit,
Du weiß genug um das zu lösen.

Gruß Sebastian

toeoe
10.08.2005, 22:42
Ok, dann hab ich ja richtig gedacht, also dann bis morgen, werd mal schaun, ob ichs heut noch hinbekomm.

Gruß
Thomas

toeoe
10.08.2005, 23:09
Ging schneller, als ich gedacht hab :D
Zu deinen Fragen:
1. Hmm...weil die Datei nun um 0,06kb kleiner geworden ist? *g* Ich weiß es nicht, hab auch deine Posts weiter oben gelesen, aber nichts brauchbares gefunden :cry:
Vielleicht dient das auch der Geschwindigkeit des Programms. Denn wenn er schneller auf ein "reti" trifft, wird das Interrupt auch schneller wieder freigegeben. Ist aber nur reine Vermutung. :-k Kann mir das leider nicht wirklich vorstellen. Hoffe, dassde nun nicht enttäuscht von mir bist, weil ich eine so leichte Frage nicht beantworten kann 8-[
2.
.include "m8def.inc"

.equ time = 256-90 ;Damit wird der Timer vorgeladen
.equ LED = PB2 ;LED an B.2
.def tmp = r16 ;Mein Universallregister
.def zaehler = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp pruefZaehler ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, LED ;B.2 als Ausgang
cbi PORTB, LED ;B.2 auf LOW stellen -> LED aus am Anfang

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) | (1<<CS00) ;prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time ;Hier wird der Timmer vorgelaen und zwar mit 255-90
out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
sei ;Interrupts zulassen

loop:
cpi zaehler, 0b00101000 ;Wenn Zählregister = 14 ist
breq zeitum ;dann spring zu "zeitum:"

rjmp loop ;Immer wieder selbst aufrufen -> Endlosschleife

zeitum:
clr zaehler ;Zählregister auf 0 setzen
sbic PINB, LED ;überspringe, wenn B.2 = 0 ist (LED aus?)
rjmp zeitum1 ;wenn B.2 = 1, dann spring zu "zeitum1:"

zeitum0:
sbi PORTB, LED ;B.2 = 1 setzen -> LED an
rjmp loop ;wieder zur loop springen

zeitum1:
cbi PORTB, LED ;B.2 auf 0 setzen -> LED aus
rjmp loop ;wieder zur loop springen

pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
ldi tmp, time ;Hier wird der Timer vorgelaen und zwar mit 255-90
out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
reti ;wieder zurück, wo du hergekommen bist

Wenigstens hab ich die programmiertechnische Aufgabe gelöst, ich hoffe doch richtig ;)

Gruß
Thomas

Achja, stimmt der Kommentar bei dieser Zeile? :?:

out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt

SprinterSB
11.08.2005, 11:03
Eine teufliche Fußangel hast du noch drin.

Da ist zunächst deine Hauptschleife:
loop:
cpi zaehler, 0b00101000 ;Wenn Zählregister = 14 ist
breq zeitum ;dann spring zu "zeitum:"

rjmp loop ;Immer wieder selbst aufrufen -> Endlosschleife


Das sieht ganz unschuldig aus.
Jetzt stell dir vor, nach dem cpi gibt es einen IRQ (Interrupt ReQuest), daß also eine Interrupt Anforderung vorliegt.

Mit cpi hast du das SREG (Status Register) verändert.
Es hat verschiedene Flags, die das Ergebnis des Vergleiches darstellen wie 'größer als', 'gleich', etc.

Danach testest du mit breq das Z-Flag (Zero). Es ist gesetzt, wenn zahler-0b00101000 gleich 0 ist. In diesem Falle springst du nach zeitum.

Aber erst kommt deine Interrupt Service Routine (ISR) zum Zug:
pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
ldi tmp, time ;Hier wird der Timer vorgelaen und zwar mit 255-90
out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
reti ;wieder zurück, wo du hergekommen bist

Das inc erhöht zaehler um 1 und setzt u.a. das Z-Flag:
Z=1, falls das Ergebnis=0 ist; Z=0 sonst.
Die darauf folgenden Befehle tasten das Z nicht mehr an, und nach dem reti landest du beim breq von loop.

Das breq wird also dann verzweigen, wenn
- zaehler=0b00101000 (=0x28 = 40) ist, und es nach den cpi kein IRQ gibt oder
- zaehler=0b11111111 ist in loop, und es nach dem cpi ein IRQ gibt.

Auf das SREG kannst du mit in und out zugreifen, da fällt dir bestimmt was ein...

Gruß, Georg-Johann

toeoe
11.08.2005, 12:23
Ich versteh leider nicht, was du meinst :(
Ich hab doch gar kein Statusregister mehr. Das haben wir doch rausgenommen. Und funktionieren tuts ja auch, also die LED blinkt ja im 1 Sekundentakt.
Vlt. kannste das ja nochmal kindergerecht erläutern, auch wenn mein Alter was anderes sagt *g*

Gruß
Thomas

SprinterSB
11.08.2005, 12:36
Da hast du was verwechselt.

SREG ist ein Hardware-Register, das alle AVRs haben. Wenn du zB ein sei Befehl machst, setzt du dadurch das I-Bit im SREG.
Andere Hard-Register sind zB PORTB, SPL, SPH, ...
Schau mal in das Kurzdatenblatt zum Mega8 (http://atmel.com/dyn/resources/prod_documents/2486S.pdf). SREG ist das erste Register im IO-Bereich ("Register Summary").
Welche Flags im SREG durch einen Maschinenbefehl verändert werden, steht in der Tabelle zu "Instruction Set Summary" in der Spalte "Flags".

toeoe
11.08.2005, 12:44
Axo, hab nu auch deinen Post verstanden. Also du meinst, direkt nach dem Befehl cpi, wenn dort die Interrupt-Routine aufgerufen wird? Nun hab ich das dann so geändert, bin mir aber nicht sicher, ob das so richtig ist. Die LED blinkt aber immer noch im 1 Sekundentakt.

.include "m8def.inc"

.equ time = 256-90 ;Damit wird der Timer vorgeladen
.equ LED = PB2 ;LED an B.2
.def tmp = r16 ;Mein Universallregister
.def statusreg = r17 ;Mein Statusregister
.def zaehler = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp pruefZaehler ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, LED ;B.2 als Ausgang
cbi PORTB, LED ;B.2 auf LOW stellen -> LED aus am Anfang

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) | (1<<CS00) ;prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time ;Hier wird der Timmer vorgelaen und zwar mit 255-90
out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
sei ;Interrupts zulassen

loop:
in statusreg, SREG ;SREG sichern
cpi zaehler, 0b00101000 ;Wenn Zählregister = 14 ist
breq zeitum ;dann spring zu "zeitum:"

rjmp loop ;Immer wieder selbst aufrufen -> Endlosschleife

zeitum:
clr zaehler ;Zählregister auf 0 setzen
sbic PINB, LED ;überspringe, wenn B.2 = 0 ist (LED aus?)
rjmp zeitum1 ;wenn B.2 = 1, dann spring zu "zeitum1:"

zeitum0:
sbi PORTB, LED ;B.2 = 1 setzen -> LED an
rjmp loop ;wieder zur loop springen

zeitum1:
cbi PORTB, LED ;B.2 auf 0 setzen -> LED aus
rjmp loop ;wieder zur loop springen

pruefZaehler:
inc zaehler ;Zählregister um 1 erhöhen
ldi tmp, time ;Hier wird der Timer vorgelaen und zwar mit 255-90
out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
out SREG, statusreg SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist

Gruß
Thomas

SprinterSB
11.08.2005, 13:08
Und funktionieren tuts ja auch, also die LED blinkt ja im 1 Sekundentakt
[-X Daß die LED im Sekundentakt blinkt, bedeutet nicht, daß das Programm korrekt ist! Du weisst nur, daß das Programm nicht korrekt ist, wenn die LED nicht blinkt wie sie soll...

In deinem Prog tauchen Interrupts nicht allzu oft auf. Timer0 muss überlaufen (90 Takte) und hat einen Prescaler von 1024.
Nen Timer0 IRQ gibt's also nur alle 92160 Takte. Und falls er nicht nach dem cpi ausgelöst wird, stört er auch nicht.

Das fiese an Fehlern in der Interrupt-Programmierung ist, daß diese Fehler oft nur sporadisch zuschlagen.
Es ist auch denkbar, daß die Ausführungszeit deines Hauptprogramms eine gerade Anzahl von Takten braucht und dadurch nie ein IRQ direkt nach dem cpi entsteht.
Wenn du aber irgendwo in deinem Prog eine Kleinigkeit änderst, geht es womöglich nicht mehr, weil sich dadurch die Laufzeit minimal verschiebt und dann die IRQ eben doch nach dem cpi ausgelöst wird.

Dann wirst du dir nen Wolf suchen, weil dein Programm vorher ja "korrekt" war und du denkst, der Fehler liegt an deiner kleinen Änderung. ](*,)

toeoe
11.08.2005, 13:16
Axo, ok, das wär natürlich ne Heidenarbeit, den Fehler dann zu finden, wieder was dazu gelernt :)
Ist denn meine Änderung so richtig, weiß net, ob du sie gefunden hast, also hab in der ersten Zeile im "loop:" das hier dazugeschrieben:

in statusreg, SREG ;SREG sichern
Und vor dem reti in "pruefZaehler:" das hier;

out SREG, statusreg SREG wiederholen
Und natürlich oben mein "statusreg" mit .def definiert.

Gruß
Thomas

SprinterSB
11.08.2005, 13:20
Ok, im Hauptprogramm nutzt das nichts. Das muss schon die Interrupt-Routine wasserdich machen:

interrupt:
Originalzustand sichern
Eigentlicher Code
Originalzustand restaurieren
reti

toeoe
11.08.2005, 13:26
Axo, also loop bleibt gleich und verändern tut sich dann nur noch preufZaehler:

pruefZaehler:
in statusreg, SREG
inc zaehler ;Zählregister um 1 erhöhen
ldi tmp, time ;Hier wird der Timer vorgelaen und zwar mit 255-90
out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
out SREG, statusreg
reti ;wieder zurück, wo du hergekommen bist

SprinterSB
11.08.2005, 13:32
Jepp, that's it!

SprinterSB
11.08.2005, 13:55
Eine kleine Verbesserung des Codes gibt's noch, wenn du in loop nicht bei Gleichheit rausspringst, sondern bei Ungleichhiet zurück nach loop :-)

Gruß, Georg-Johann

toeoe
11.08.2005, 14:04
Axo, weil nachher dann eh "zeitum:" kommt, oder? Najo, ok, das ist aber wirklich nur ne Kleinigkeit ;)

Florian
11.08.2005, 14:15
Hallo ihr Drei!
Der Thread mausert sich ja langsam zu einem richtig komplexen Tutorial!
Herzlichen Glückwunsch, vor allem Dir, Thomas, Du hast das Glück alles beigebracht zu bekommen und entwickelst Dich rasch zu einem "Fortgeschrittenen"! ;o)
Macht weiter so!

PS:
Ich habe eben die ganzen verpassten Posts nachgelesen, ist ja schon einiges! *lol*

toeoe
11.08.2005, 14:18
Hehe, dank dir Florian für das Kompliment.
Aber fühl mich trotzdem "schlecht", weil ich Sebastians 1. Frage nicht beantworten kann. *g*

Florian
11.08.2005, 14:22
Ich denke er ziehlt darauf ...
Denk' nochmal nach, derzeit hast Du nur eine Interruptquelle, was würde bei mehreren passieren bzw. wenn diese alle nahezu gleichzeitig ... *auf den mund hau*

toeoe
11.08.2005, 15:16
Ahh, ok, hab die Antwort, poste sie nachher, muss essen *g*

So, nun die Antwort:
Also die Interruptroutinen sollten so kurz wie möglich gehalten werden, da, wenn er in einer Routine drinne ist, keine andere aufgerufen werden kann.
Und wenn ein Interrupt zu dem Zeitpunkt ausgelöst wird, wo er gerade in einer anderen Interruptroutine drinne ist, dann kann das andere Interrupt nicht ausgelöst werden.
Denn nur nach dem Befehl "reti" können Interrupts wieder ausgelöst werden, deshalb muss dieser Befehl so schnell wie möglich auftreten.

Hoffe, das ist so richtig, ist ein wenig durcheinander, aber man muss ja auch erstmal mit den Fachbegriffen klarkommen

Gruß
Thomas

Florian
11.08.2005, 16:31
Hallo Thomas!
Das klingt schon gut!

Denn nur nach dem Befehl "reti" können Interrupts wieder ausgelöst werden, deshalb muss dieser Befehl so schnell wie möglich auftreten.Naja, so ganz richtig ist das nicht, aber im Prinzip schon!
reti ist einer der Befehle, nach denen Interrupts wieder ausgelöst werden können!
Welchen Befehl kennst Du für diese Funktion noch?
Du hast ihn schon immer selbstverständlich verwendet! ;o)

toeoe
11.08.2005, 16:34
Da gibt es noch "sei", den ich am Anfang vom Programm ausführe, also nachdem ich den Timer0 intitialisiere.

Bin gespannt, wann Sebastian wieder kommt, hab mich schon ein wenig über Timer1 informiert ;)

izaseba
11.08.2005, 16:50
Hallo, Leute

So wie ich sehe hast Du Thomas eine super Antwort gegeben, das ist der Grund!
Wenn Du anfängst im Interrupt delays zu machen, hin und her zu springen,
kommt kein anderer Interrupt zum Zuge, :(

Schön, daß Sprinter die Sache mit SREG angesprochen hat,
aber es war wieder meine Schuld, ich habe die Rettung von SREG zwischendurch
rausgeschmissen(sehe die Programme weiter oben), um den Thomas damit nicht zu verwirren, ich wollte damit heute kommen, nachdem er diese Aufgabe gelöst hat :-& .
Naja ich werde den SREG hier nicht weiter ansprechen, das kann sich jeder unter
http://www.avr-asm-tutorial.net/avr_de/beginner/index.html durchlesen, ich sage nur, daß das ergebnis jeder mathematischen Operation, die man macht im SREG abgebildet wird.

Thomas Du hast ja Studio, dann schreib Dir einfach ein Miniprogramm, wo zb brne vorkommt

zb
ldi tmp,0x80
cpi tmp,0x90
brne hierhin

naja so was in der Art, dann geh Zeile für Zeile Durch, und beobachte den SREG und vor allen wenn Du die Werte änderst, was dann passiert, dann lese nach, was Die Bits von SREG zu bedeuten haben.
Der µC macht seine Sprünge anhand des SREGs und wenn Du ihn im Interrupt änderst,
dann hat er ganz anderen Wert wenn er zurückkommt,

Thomas hast Du schon was von Stack gehört ?

toeoe
11.08.2005, 16:58
Tachschen Sebastian :)

Jo, vom Stack hab ich schonmal was im Tutorial gelesen. Das ist doch, wenn ich Unterprogramme aufrufe, dann merkt er sich im Stack, wo er hergekommen ist, wenn ich vom Unterprogramm wieder ein Unterprogramm aufrufe, schreibt er es in den Stack auch wieder rein. Stößt er dann auf den Befehl "ret", dann springt er Schritt für Schritt wieder zurück (in der umgekehrten Reihenfolge).

Hoffe mal, das stimmt einigermaßen so.

Gruß
Thomas

[edit]
Das mit dem SREG versteh ich nun auch viel besser durch die Simulation im AVR Studio :)

izaseba
11.08.2005, 17:22
Ja genau, so in etwa sieht es aus.

Stack wird einfach am Ende von SRAM eingerichtet ( deswegen dieses RAMEND)
und wird benutzt um irgendwas eben kurz zwischenzuspeichern.

Die erste Möglichkeit hast Du schon genannt, bei rcalls speichert der µC die Adresse, wo er anschließend zurückspringen soll auf dem Stack und bei ret holt er sie sich wieder um zu wissen wo jetzt hin.
Das macht er auch bei Interrupts, da landet die Rücksprungadresse auch auf dem Stack.
Eine Eigenschaft vom Stack ist aber, daß ich nur was obendrauf werfen , oder nur was von oben hollen kann. an Sachen die dazwischen liegen komm ich nicht dran, ist wie ein Stapel
Bettonplatten, ich muß immer von oben entnehmen, oder obendrauf legen, kapito?

Jetz kommt eine schöne Sache, wo man den Stack benutzen kann.
bei Eintritt in die Interruptroutine hast Du den SREG in einen Register zwischengespeichert, gut, nur was machst Du wenn Du keinen Register mehr übrig hast?
Dann nimmst Du den Stack!

es geht so:

in tmp,SREG ;kopiere Inhalt von SREG in tmp
push tmp ;lege Inhalt von tmp auf den Stack
............ Hier folgt das Programm
pop tmp ;Hole die Spitze, also die oberste Stelle vom Stack und schreibe es in tmp
out SREG,tmp ; Gerettet!
reti

aber bevor wir einen RIESEN großen Fehler machen nochmal
das Gleiche, nur etwas kommt noch dazu:

push tmp ;lege tmp auf den Stack
in tmp,SREG ;kopiere Inhalt von SREG in tmp
push tmp ;lege Inhalt von tmp auf den Stack
............ Hier folgt das Programm
pop tmp ;Hole die Spitze, also die oberste Stelle vom Stack und schreibe es in tmp
out SREG,tmp ; Gerettet!
pop tmp ;Auch geretet!
reti

Kannst Du sagen, wo der unterschied ist, und was passiert wenn man den erste Code nimmt?

toeoe
11.08.2005, 17:29
Puhh...
also ich bevorzuge den 2 Code ;) Warum? Weil beim 1 Code der aktuelle Inhalt von tmp verloren geht, nachdem er wieder aus der Interruptroutine rausspringt. Denn wir speichern den aktuellen Code von tmp nirgends. Wir überschreiben ihn gleich mit SREG.
Das ist beim 2 Code richtig gelöst. Da bekommen wir alles wieder, nachdem die Interruptroutine durchlaufen ist.

izaseba
11.08.2005, 17:46
So ist es, wie Du siehst ist der Stack eine echt gute Sache um eben irgendwas zwischenzuspeichern, nur auf eine Sache muß man wirklich achten, wenn man den Stack
verwendet, und zwar muß die Anzahl der push der Anzahl der pops entsprechen, sonst müllst Du Dir den Ram zu und irgendwann ist der komplett zu.
Was noch schlimmer ist liegen bei Mega 8 im Ram bis zu Adresse 0x60 (hoffe ich, habe keine Lust jetzt genau nachzuschauen) Inhalte aller Register also R1 - R31 alle Ports, PINS ,DDRs usw.
da schreibt der µC gnadenlos rein! Es wird nicht geprüft, was da passiert.
Welche Folgen das hätte kannst Du Dir vorstellen.

toeoe
11.08.2005, 17:52
Jo, denk schon, Er ändert dann im schlimmsten Fall die Register und/oder die Ports, die ich auch verwende und die LEDs oder was auch immer blinken nicht so, wie ich es möchte.

izaseba
11.08.2005, 18:40
Jo, genau, der AVR läuft Amok , ausprobiert habe ich es noch nicht,
also aufpassen, und vor allem auch auf die Reihenfolge achten, so wie das schon in
der Bibel steht, die Letzten werden zu Ersten,
beim zurückholen zuerst das was man als letztes abgelegt hat.

Hast Du noch Fragen zu den Sachen, die Du bis jetzt gelernt hast?
Hast Du alles verstanden, ich fasse nochmal zusammen:

Programmablauf -

fängt bei Adresse 0x000 an und arbeitet sich zeile für zeile nach unten

Labels also diese loop: oder was Du Dir da ausdenkst sind keine
Funktionen in dem sinne, sondern nur kleine Markierungen im
Programm um die adresse später einfacher anzuspringen

Register -

Register R1-R31 können mit Werten geladen werden um sie dann weiter in andere Register,Ports zu schreiben.

Register kann man als Variablen nutzen.

Bedeutung von .org .def .equ und wie sie uns das Leben einfacher machen.

Bitmanipulationen mit (1<<PB1) z.B oder (1<<PB1) | (1<<PB2)

interrupts, gefahren und nutzen

SREG Bedeutung
Stack Bedeutung und gefahren.

Timer , Takt vom Quarz und wie man daraus irgendeinen anderen Takt bekommt

Branches (breq,brne usw.), wie sie arbeiten und was sie mit SREG zu tun haben

Waren das alle Themen?
Wie ich sehe, haben wir schon jedemenge durch was ?

Es ist wirklich wichtig, daß Du das alles verstehst!

P.S.
Es sind keine Fragen, nur eine Zusammenfassung

Gruß Sebastian

toeoe
11.08.2005, 18:48
Hui, ist wirklich schon ne Menge, denkt man ja gar nicht, dass man schon so viel kann.
Soweit ist alles klar, außer (das muss ja immer sein *g*):
Bitmanipulationen mit (1<<PB1) z.B oder (1<<PB1) | (1<<PB2)
Also das erste ist klar. Er verändert das Bit an der Stelle PB1. Dann würde daraus dann 0b00000010 das hier werden, oder eben dann wieder alles auf 0, je nach Befehl.

"(1<<PB1) | (1<<PB2)" <-- bei dem hier. Ist das sone Art UND-Befehl?
Also wird dann daraus 0b00000110 das hier?

[edit]
So, ich muss dann los zum bowlen. Bin heut abend so gegen 23:00 - 23:30 denk ich wieder hier.

Gruß
Thomas

izaseba
11.08.2005, 19:07
"(1<<PB1) | (1<<PB2)" <-- bei dem hier. Ist das sone Art UND-Befehl?
Also wird dann daraus 0b00000110 das hier?


Im Prinzip ist das schon richtig was Du sagst, also 2 mal die 1 schieben ergibt
00000110 aber es ist kein UND Befehl sondern ein ODER

hier eine Erklärrung :

byte 1 : 00000011
byte 2 : 00000010

ODER Verknüpfung:
Wenn ein Bit von Byte 1 ODER ein Bit von Byte 2 1 ist, soll das Ergebnis auch 1 sein!

mithin wenn Du die beiden Bytes mit ODER verknüpfst bekommst Du 00000011

UND Verknüpfung:
Wenn ein Bit von Byte 1 UND ein Bit von Byte 2 1 ist, soll das Ergebnis auch 1 sein!

mithin wenn Du die beiden Bytes mit UND verknüpfst bekommst Du 00000010

Das ist auch sehr wichtig und nützlich!
Gibt auch extra Befehle OR AND ORI ANDI

Gruß Sebastian

toeoe
11.08.2005, 19:10
Sehr anschaulich, danke :)
So, nu aber zum bowlen, wünscht mir Glück ;)

Bis denne
Thomas

izaseba
11.08.2005, 19:12
Jo viel Spaß beim Bowlen

toeoe
11.08.2005, 22:28
So, da bin ich wieder :)
Ich hoffe, du bist noch wach, Sebastian? :(

izaseba
11.08.2005, 22:45
Ja ich bin noch wach,
Bin hier noch was am Tüfteln O:)

Hast Du das mit den OR und AND verstanden?
Die dinger weden Dich auch begleiten, wenn Du µC programmierst...

Wenn ja dann dürfte das hier kein Problem sein

Was bleibt im tmp über, wenn Du folgende Operationen ausführst :

ldi tmp,0xFF
andi tmp,0x80

ldi tmp,0xA0
ori tmp,0x05

Ich schreibe extra hexadezimal, damit Du damit auch vertraut wirst.
Ich hoffe, Du hast einen Taschenrechner wo Du das umwandeln kannst.

Übrigens, wir haben den Code für das blinken mehrmals geändert,
Damit hast Du
1. gesehen, daß es mehrere Möglichkeiten gibt um das Problem zu lösen
2. Du hast schon ein paar Probleme kennengelernt, die bei der Arbeit
mit Interrupts auftretten können, und die Wege um sie umzugehen.

Nicht das Du denkst, wir machen nur an einem Code rum,
es hatte alles schon seine Richtigkeit, sowas wirst Du in dieser Form
in keinem Tutorial finden...

P.S. wie war das Bowlen?

Florian
11.08.2005, 22:50
N'Abend zusammen!

Ja ich bin noch wach,
Bin hier noch was am TüftelnIst es das, was ich denke? *g*
Meine Schwester sucht leider gerade im Internet nach freien Studenten-WGs in Bremen, deshalb war ich heute kaum online!
Dementsprechend konnte ich auch den Code noch nicht schicken, das werde ich dann morgen machen!

izaseba
11.08.2005, 23:00
Hallo Florian,
lass Dir ruhig Zeit, heute werde ich eh nicht mehr viel machen, muß mich erst etwas einlesen
aber ich muß schon sagen, daß slave etwas komplizierter als master ist.
Aber wir lieben ja Herausforderungen, was ?

Und was hälst Du von Thomas?

Sollen wir Ihn schon auf 16 Bit Zahlen und Timer 1 loslassen, oder ist er noch nicht soweit?
8-[

Gruß Sebastian

P.S. Thomas keine Angst , es ist ja nur Spaß

toeoe
11.08.2005, 23:10
Ich benutze dafür den Windows-Rechner, da mein Taschenrechner sowas nicht kann.
Bowling war gut. War ja nur mit meiner Freundin, das erste Spiel hat sie gewonnen, aber die nächsten 4 Spiele dann ich :) (Brauche ja immer 1 Spiel zum warm werden *g*)

Bin mir noch net so sicher mit ANDI und ORI. Aber könnte man sich die ersten beiden Zeilen nicht sparen, wenn man dann mit ldi weitermacht? Denn bei ldi überschreibt er ja eh den ganzen Wert, ohne irgendwas zu rechnen, oder?
Also nach den Zeilen:
ldi tmp,0xFF
andi tmp,0x80
ist der Wert von tmp: 0b10000000 also 0xFF
Nach den Zeilen:
ldi tmp,0xA0
ori tmp,0x05
ist der Wert von tmp: 0b10100101 also 0x00

Ich bin mir aber wie gesagt nicht sicher.
Wie komm ich zu den Ergebnissen?
Hab sie erstmal in binär umgerechnet, da kann ichs besser vergleichen

Also bei der ersten Zeile wird ja in tmp 0b11111111 geladen. Und das dann mit 0b10000000 verglichen(?), Wenn BEIDE Bits von beiden 1 ist, dann ist das Ergebnis auch 1:
0b11111111
0b10000000
-------------
0b10000000

Bei der dritten Zeile wird binär 0b10100000 in tmp geladen und mit 0b00000101 verglichen(?). Wenn EIN Bit von beiden 1 ist, dann ist das Ergebnis auch 1:
0b10100000
0b00000101
--------------
0b10100101

Obwohl ich das nun bezweifle, dasses richtig ist :/

[edit]
Ich und Angst? 8-[ *gg*

Florian
11.08.2005, 23:12
Hallo Sebastian!
Ja, Herausforderungen sind schön, es sei denn, sie sind zu herausfordernd, so wie mein derzeitiges Projekt, an dem ich jetzt schon seit 2 Jahren intensiv arbeite! ;o)
Ich denke wir sollten erstmal mit Rechnen mit 16 Bit bzw. lpm weitermachen, Timer1 sollte dann davon das Endprodukt sein!
Z-Register hochzählen usw.!
Was hälst Du davon?
Ansonsten gibt es ja noch die schönen IRQ's!?
Serielle Schnittstelle ist aber glaube ich noch zu früh, damit warten wir besser noch etwas!

izaseba
11.08.2005, 23:23
Aber könnte man sich die ersten beiden Zeilen nicht sparen, wenn man dann mit ldi weitermacht?

naja andi und ori machen ein AND bzw. OR auf einem Register,
ich muß Ihn ja laden
ldi tmp,0xFF <- Ist mein Ausgangswert

somit habe ich den ersten wert in tmp

und bei andi tmp,0x80 <- hiermit will ich Verknüpfen

wird der tmp mit 0x80 Verknüpft und das Ergebnis bleibt im tmp drin

Der Ansatz war schon richtig, die Zahlen in binär umzurechnen, untereinander zu schreiben
und dann gucken, aber Du hast was verwechselt,
AND heißt Bit vom erstem UND zweiten -> 1 sonst 0
OR heißt Bit vom erserm ODER zweitem -> 1 sonst 0

mithin hast Du bei AND nur dann 1 wenn beide 1 sind -> sonst 0
bei or hast Du 1 wenn einer von beiden 1 ist sonst wenn beide 0 -> 0

toeoe
11.08.2005, 23:26
Jo, hab ich noch editiert, aber anscheinend warst du dann schon beim Schreiben, habs also noch selbst gemerkt, puh *g* :)
Gut, dann hab ich das schonmal verstanden :)
Und wie ich auch gelesen hab, benutzt man andi und ori bei Verknüpfen von Registern mit Konstanten? und and und or beim Verknüpfen von Register und Register?

izaseba
12.08.2005, 00:03
Schön, daß Du selber drauf gekommen bist,
Unterschied zwischen or und ori hast Du vollkommen recht,
Ich hab einfach nur die mit i am ende genommen, weil ich zu Faul war zwei Register zu benutzen. :-b

Gut.
Du möchtest sicher was zu tun haben, also irgendwas schönes Programmieren.

Dann hätte ich da 1 Sache.
Ich glaube auf Deinem Board ist ein Piezo drauf, so ein Summer, schwarz rund.
Du kannst Ihn auch mit unserem Timerprogramm ansteuern!
Nur gib Ihm nicht 1 Hz, so wie wir das bei der Diode gemacht haben, sondern z.B.
1 kHz, oder 4 kHz
Der müßte dann richtig laut krach machen.

Bei den Frequenzen mußtest Du dicke mit Timer0 auskommen, d.h. den einfach so vorladen, daß Du 1kHz hast oder 4kHz oder weiß was ich.

Dann geh hin und suche im Dattenblatt nach Timer 2 , es ist auch ein 8 Bit Timer, den Du genauso benutzen kannst wie den Timer 0, die Register von heißen sogar genauso, da ist einfach immer eine 2 im Namen, der hat auch prescaler usw.
Jetzt benuze Ihn so das er sagenwirmal jede 0,5 Sekunde einen Interrupt auslöst.
in dem Interrupt kannst Du dann eine hilfsvariable (Also einen freien Register) nehmen,
ein Bit als sag ich mal Umschalter nehmen und immer zwischen 1 und 0 schalten.
Im Timer 0 prüfst Du diesen Bit, und jenachdem welchen Zustand er hat lädst Du den Timer 0 mit anderen Werten, einmal für 1kHz und einmal für 4 kHz z.B.

Naja, das ist jetzt eine Hammeraufgabe,
was Du brauchst sind 2 Timer (die laufen unabhängig voneinander)
2 Interrupt Routinen
Irgendein Statusregister(aber nicht mit SREG vewechseln) wo Du Dir merkst welchen Ton Du erzeugen sollst.

Ich finde es ist aber doch nicht zu schwer un machtbar.

Eine Anmerkung

Du mußt unbedingt die Reienfolge von .org blabla
einhalten!
ganz oben .org 0x000
und die Interrupts in der Reihenfolge , wie sie in m8def.inc stehen sonst klappt es nicht !

Und mit dem Vorschlag von Florian vonwegen rechnen usw. muß ich mich mit ihm
noch absprechen, dazu müßen wir ja ein schönes Beispiel finden, sonst wird es zu langweilig.

Gruß Sebastian

P.S. Ich hoffe, daß Du jetzt nicht wegrennst !!
Ich gehe schlafen , gute Nacht, würde Dir gerne noch ein paar Tips geben, aber die bekommst Du sicher vom Florian oder Sprinter, wenn Du nicht weiterkommst

toeoe
12.08.2005, 00:12
Hui, wegrennen tu ich bestimmt nicht, dann lern ich doch nichts ;)

Die Aufgabe klingt sehr interessant. Im Kopf ist sie schon gelöst, also denke auch, dass ich sie hinbekomme. Werd mich aber auch erstmal schlafen legen.

Aber was du mit dem Satz hier meinst, weiß ich nicht:

Nur gib Ihm nicht 1 Hz, so wie wir das bei der Diode gemacht haben, sondern z.B.
1 kHz, oder 4 kHz
Ich frag mich, wo ich der Diode 1Hz gegeben hab :oops:

Also dann bis morgen und Gute Nacht
Thomas

[edit]
Also verschiedene Frequenzen bekomm ich schonmal hin *Piepsen im Ohr hab* *gg*
Muss dann mal rechnen :)
Morgen werd ich dann hoffentlich nen Code zeigen können.

toeoe
12.08.2005, 00:39
Ich bekomm zwar verschiedene Frequenzen hin, aber ich weiß nicht, wie ich das ausrechnen soll. Die Rechnung, wo wir das mit der 1 Sekunde ausgrechnet haben, kann ich darauf gar nicht anwenden...also da bräucht ich dann glaub ich schon ein wenig Hilfe :/

Florian
12.08.2005, 08:52
Guten morgen! *gähn*

Ich frag mich, wo ich der Diode 1Hz gegeben habDas hast Du doch selber einmal schon festgestellt, was wir mit 1 Hz meinen!
1 Hz = ein Zustandswechsel pro Sekunde
Das würde bedeuten, dass Deine LED im Sekundentakt an und aus geht, also blinkt! ;o)
Bei Piezo's ist das aber nicht so sinnvoll, da die Frequenz viel zu niedrig für einen Ton ist, Du bekämst nur ein knacken zu hören, wenn überhaupt! *g*
Ich habe immer eine kleine Tabelle bei mir liegen, wenn ich Töne programmiere:
http://www.pianotip.de/frequenz.htm

Um die Frequenz einzustellen musst Du erstmal den Vorteiler herunterschrauben, sonst kommst Du ja niemals auf diese Frequenzen! *g*
Jetzt musst Du den Vorteiler nur in die Formel einsetzen und einen passenden Wert für das andere Register finden!
Kleiner tipp, derzeit hast Du einen Vorteiler von 1024!

SprinterSB
12.08.2005, 09:50
Guten morgen! *gähn*

Ich frag mich, wo ich der Diode 1Hz gegeben habDas hast Du doch selber einmal schon festgestellt, was wir mit 1 Hz meinen!
1 Hz = ein Zustandswechsel pro Sekunde
Das würde bedeuten, dass Deine LED im Sekundentakt an und aus geht, also blinkt!

Hmmm...
Wenn eine LED 1x pro Sekunde ihren Zustand wechselt, dann blinkt sie mit 0.5Hz. Bei Hz geht sie 1x an und 1x aus pro Sekunde.
Sonst ist dein Pieper nachher um 1 Oktave verstimmt.

Greets,

Georg-*haarespalt*-Johann

Tekeli
12.08.2005, 10:06
Hallo an alle,

erstmal einen dicken Dank an Sebastian und Florian!

Ich mache unauffällig bei Eurem Tuturial mit :).
Da ich hier kein Board habe, benutze ich meinen Asuro dazu.
Für die nächste Aufgabe:), die mit dem Piezo, habe ich aber eine Frage, die vielleicht ein bißchen von dem Thema abweicht (Ich weiß, daß Sebastian auch einen Asuro hat).
Ist es möglich einen Piezo beim Asuro irgendwie anzuschliessen?
Einen Piezo habe ich schon. (Vom alten Mainboard abgelötet:) drauf steht KC-1206).

Nochmals vielen Dank für Eure Mühen!

Best wishes

toeoe
12.08.2005, 10:47
Ok, danke für die Info, werd mich dann mal ransetzen und bei Problemen sicher wieder melden :)

Gruß
Thomas

Florian
12.08.2005, 10:59
Hallo Georg-*haarespalt*-Johann! ;o)
Wie soll ich Dich eigentlich nennen, Georg, Johann oder Georg-Johann, was ist Dir am liebsten?
So, wie Du es jetzt verbessert hast meinte ich's hab's aber nicht geschrieben! *lol*
Naja, genug der Ausreden! ;o)

@ Tekeli:
Sehr schön, dass Du hier mitmachst!
Wenn Du Fragen hast kannst Du natürlich einfach fragen, auch wenn Thomas unser Privatschüler ist! *lol*

Viel Spass weiterhin!

Überigens auch vielen Dank an Georg-Johann! :o)

toeoe
12.08.2005, 12:54
So, ich fang also erst jetzt an. Mussten erstmal noch Essen einkaufen und hab eh grad nicht den Kopf frei, da wir ne Rechnung von der GEZ bekommen haben (50€ für 3 Monate) Wo sollen wir die nur hernehmen :( Najo, ich versuch jetzt mal, die Frequenz auszurechnen.

Gruß
Thomas

Nur erstmal zum Verständnis, dass ich auch die richtige Frequenz habe. Meiner Meinung nach, kann ich mit meinen 3,6864 Mhz nur eine Frequenz von 3600 Mhz hin, denn ich hab nun folgenden Code.
Der Timer wird dann mit "256-1" vorgeladen (mehr geht ja nicht bzw. niedriger). Den Prescaler kann ich doch nicht verändern, oder?

ldi tmp, (1<<CS02) | (1<<CS00) ;prescaler ist 1024
Wieso issn das 1024, das ist doch binär --> 0b00000101 --> also 5, oder?
Naja, und dann hab ich noch in der loop "cpi zaehler, 0b00000001", also wird immer nur einmal durchlaufen. Ergibt eine Frequenz von 3600 Mhz, hört sich auch ziemlich hoch an, also könnte hinkommen. Aber ich komm dann leider nicht auf 4 KHz.

[edit]
Also mein Zähler läuft immer nur 1 durch. Also meine Formel lautet dann wohl:

3686400 / 1024 / 1046 = x
Das x komm dann beim Timer rein -->
.equ time = 256-x
Denk mal, dass das so richtig ist. Hab verschiedene Frequenzen ausprobiert, aber wie gesagt, mehr als 3600 Mhz bekomm ich dann net hin.

SprinterSB
12.08.2005, 13:22
@Florian:

Bitte, bitte :-)

Ich hör auf 'Georg' und 'Johann', aber Johann ist ok.
Vorerst wirst du mich eh nicht mehr ansprechen, denn ich bin ab morgen im Urlaub *freu*.

toeoe
12.08.2005, 13:36
Hmm..ich komm nicht klar :( Ich kann doch nicht 2mal das Register TIMSK belegen, dann laufen die Timer doch falsch. Ich muss ja für Timer0 UND für Timer2 das Register TIMSK beschreiben, aber dann läuft ja alles falsch.

Florian
12.08.2005, 14:08
Hallo Johann!
Viel Spass im Urlaub! ;o)

Hallo Thomas!
Im Datenblatt findest Du eine Beschreibung der jeweiligen Timer!
Dort steht dann z.B. auf Seite70 das:

toeoe
12.08.2005, 14:17
Axo, ok, jetzt versteh ich, wie die 1024 zustande kommen.
Und zum TIMSK: Überschreibt er das nicht? Also weiß der Controller von selbst, wo das hin soll?

Und was ich grad getestet hab. Ich hab das letzte Programm genommen, wo die LED im Sekundentakt blinkt. Hab anstelle von Timer0 den Timer2 genommen, sonst nichts geändert, und dann blinkt die LED nimmer im Sekundentakt, sondern schneller. Also es gibt dann wohl doch einen Unterschied zwischen Timer0 und Timer2.

Man ej, heut ist echt ein scheiß Tag, bekomm gar nichts hin... :( GEZ nervt, kein Geld und programmieren klappt heut auch nicht.

[edit]
Ahh, bei Timer2 muss der Prescaller anders gesetzt werden, da wolltet ihr mir wohl einen Streich spielen ;)

Aber mit dem TIMSK ist immer noch ein wenig komisch.
Ok, TOIE0 = 0
und TOIE2 = 6
Also schreibt er ja wohl das richtige Bit auf 1. Aber trotzdem blinkt die LED nimmer, wenn ich den zweiten Timer auf TIMSK setze.

Florian
12.08.2005, 15:18
Hallo Thomas!
Könntest Du Dir wieder angewöhnen die Codes bzw. Codesegmente zu posten!? ;o)

Man ej, heut ist echt ein scheiß Tag, bekomm gar nichts hin... GEZ nervt, kein Geld und programmieren klappt heut auch nicht.Meine Güte, Du Armer, Du tust mir ja Leid! *schmunzel*
Ja, ja, die liebe GEZ! ;o)
Seit einer aus unserer Straße einen wegen Hausfriedensbruch angezeigt hat, kommt niemand mehr vorbei! *g* *ganz brav GEZ bezahl*

Kleiner Vorschlag!
Du versuchst mal Dir die Register im Datenblatt nzusehen, ich sage Dir die Seiten und Du sagst mir, wozu die ganzen Register gut sind, ok!? ;o)
Ohne diese register kann man das ganze nicht umsetzen, außer man heißt Sebastian, der bekommt das auch so nebenbei hin! *g*

toeoe
12.08.2005, 15:57
Also ich post einfach mal den ganzen Code erstmal:

.include "m8def.inc"

.equ time0_1 = 256-4 ;Damit wird der Timer0 vorgeladen - für 1KHz
.equ time0_2 = 256-1 ;Damit wird der Timer0 vorgeladen - für 4KHz
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen
.equ Summer = PB2 ;Summer an B.2
.def tmp = r16 ;Mein Universallregister
.def statusreg = r17 ;Mein Statusregister
.def zaehler = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefZaehler ;Interruptvektor für Timer2 Überlauf, hier spring
;das Programm hin, wenn der Timer überläuft

.org OVF0addr
rjmp blabla


reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten #RAM-Adresse
out SPL, tmp

sbi DDRB, Summer ;B.2 als Ausgang
cbi PORTB, Summer ;B.2 auf LOW stellen -> LED aus am Anfang

;Timer Register werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCCR2 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE2) ;Hier werden Interrupts nach Timer2 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time2 ;Hier wird der Timer vorgeladen und zwar mit 255-90
out TCNT2, tmp

;Timer Register werden belegt, hier Timer0
ldi tmp, (1<<CS02) | (1<<CS00) ;Prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time0_1 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
sei ;Interrupts zulassen

loop:
cpi zaehler, 0b00101000 ;Wenn Zählregister != 40 ist
brne loop ;dann spring wieder zurück zu "loop:"

zeitum:
clr zaehler ;Zählregister auf 0 setzen
sbic PINB, Summer ;überspringe, wenn B.2 = 0 ist (LED aus?)
rjmp zeitum1 ;wenn B.2 = 1, dann spring zu "zeitum1:"

zeitum0:
sbi PORTB, Summer ;B.2 = 1 setzen -> LED an
rjmp loop ;wieder zur loop springen

zeitum1:
cbi PORTB, Summer ;B.2 auf 0 setzen -> LED aus
rjmp loop ;wieder zur loop springen

pruefZaehler:
in statusreg, SREG ;SREG sichern
inc zaehler ;Zählregister um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgelaen und zwar mit 255-90
out TCNT2, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist

blabla:
in statusreg, SREG
ldi tmp, time0_1
out TCNT0, tmp
out SREG, statusreg
reti
Hab zwar schon alles in Summer geändert, aber zum testen hab ich trotzdem erstmal noch an der LED angeschlossen. So, wie jetzt der Code ist, blinkt die LED gar nicht. Wenn ich allerdings das hier auskommentiere, dann gehts:

;Timer Register werden belegt, hier Timer0
ldi tmp, (1<<CS02) | (1<<CS00) ;Prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time0_1 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
Und das will in mein kleine Köpfchen nicht rein ;)

Mit deinem Vorschlag bin ich zufrieden, aber ich bezweifle, dasses was bringt *g*

Gruß
Thomas

PS: Sorry, dass mein letzter Post so durcheinander war :(

Florian
12.08.2005, 16:23
Hallo Thomas!
Sei nicht unglücklich, das Leben geht weiter, wohl oder übel auch mit GEZ! ;o)

Mir ist als erstes aufgefallen, dass Du zweimal das TIMSK mit ldi -> out füllst!
Somit überschriebst Du ja das erste Mal befüllen!
Probiers mal mit einmal ldi temp , (1 << TOIE0) | (1 << TOIE2) ; out TIMSK , temp ;
Ansonsten hat das keinen Sinn!
Ich hoffe Du hast verstanden, worum es geht!?

Kleine Aufgabe:
Wie könntest Du die beiden Bits trotzdem getrennt setzen?

toeoe
12.08.2005, 16:30
Aaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhhhhhh schmarn!
Ich hasse diese Editierfunktion für Moderatoren! ;o)
Ich habe versucht noch etwas zu retten! (siehe unten)

Tschuldigung!
Florian (Moderator)

Huhu Florian! ;o)
... wenn man nicht weiß, wie man die Rechnung zahlen soll ...
Genau das war mein Problem.

Richtig?
ldi tmp, (1<<TOIE0)
out TIMSK, tep
ldi tmp (1<<TOIE2)
or TIMSK, tmp

Florian
12.08.2005, 16:41
Hi Thomas! ;o)
Scheint Dir ja schon wieder besser zu gehn! *freu*

wenn man nicht weiß, wie man die Rechnung zahlen sollAu, das ist natürlich mistig! :o( *s__t*

Genau das war mein Problem.Na dann kanns ja weiter gehn! ;o)

ldi tmp, (1<<TOIE0)
out TIMSK, tep
ldi tmp (1<<TOIE2)
or TIMSK, tmpTut mir Leid, aber selbst wenn ich die ganzen Fehler übersehe, funktioniert das nicht!
TIMSK ist leider kein vergleichbares Register!

Richtig?Nein! ;o)

[ Auf diese and & Co. wollte ich garnicht zielen, schau mal in der Instruction Set Summary nach, da gibt es zwei schöne Befehle! ]

*edit*:
Nachdem ich Dir ausversehen Deinen Text gekillt habe, habe ich auch noch Mist erzählt, der Befehl funktioniert da ja nicht, da es außerhalb der Reichweite ist! *'tschuldigung*
Heute ist wohl auch nicht mein Tag! :o(

toeoe
12.08.2005, 16:49
Mein schöner Beitrag? :P *g*

Meinst du vlt. den Befehl sbr? Aber wenn du sagst, dass TIMSK kein Register ist, dann wird sbr wohl auch falsch sein. sbi isses aber auch nicht. Hmm, andere Befehle kamen für mich auch nicht in Frage.
Aber wenn TIMSK ein vergleichbares Register wäre, würde es so mit "or" gehen, oder?

[edit]
Außerhalb welcher Reichweite? :o

Florian
12.08.2005, 16:51
Hallo Thomas!
Es tut mir unendlich Leid, mir ist das schon öfters passiert, der Knopf ist da so ungünstig! *heul*
Das war eben mein Fehler, siehe oben!
Bei dem, was Du gemacht hast, musst Du erst den Umweg über ein weiteres Register machen!

*edit*:

Außerhalb welcher Reichweite?Das erkläre ich Dir mal wann anders! ;o)

toeoe
12.08.2005, 16:56
So, nochmal, ich glaub ich habs verstanden, was du mit dem Umweg meinst ;)
Also so:

ldi tmp, (1<<TOIE0)
out TIMSK, tmp
ldi tmp1 (1<<TOIE2)
or tmp, tmp1
out TIMSK, tmp
:)

Florian
12.08.2005, 16:57
Lieber Thomas!
Ich glaube Du musst ins Bettchen! *lol*
Sieh Dir den Code nochmal genau an!

toeoe
12.08.2005, 16:58
Hab schon editiert *gg* Sorry.

Florian
12.08.2005, 16:59
;o) Nagut, jetzt ist es richtig!
So, jetzt gehts weiter mit der Grundaufgabe!

Weiß jemand, wo Sebastian ist!?

izaseba
12.08.2005, 17:01
Hallo Leute, ich sehe , daß ich hier ganz schön mit der Aufgabe aufgemischt habe.
@Thomas ,
mein Beileid, wegen GEZ, ich habe mir die Typen bis jetzt gut vom Hals gehalten,
eine Kamera an der Türe bewirkt wunder!
Und Timsk ist auch ein Register ! Nur mit or darfst Du ihn nicht behandeln!
erinnere dich zurück, wo Du mich gefragt hast was das wohl soll
(1<<PD2) | (1<<PD3)

Das ist doch eine OR Verknüpfung !!

Dann versuche das bei Timsk anzuwenden nur nicht mit PD2 und PD3 sondern was anderem...

und was Florian meint wäre auch gut geeignet schaue mal die Befehlliste durch
Du hast sbr vorgeschlagen, wäre da sbi nicht besser?

Edit, na wo ist wohl Sebastian, bis jetzt gearbeitet
Gruß Sebastian :(

toeoe
12.08.2005, 17:05
Ahh, da ist der Sebastian ja, Tach.
Jo, hab das nun soweit verstanden :)
Nun geh ich wieder zur eigentlichen Aufgabe rüber, puhh...weiß aber nicht, ob ich das heut noch schaffe, is ja ziemlich kompliziert, wies aussieht.

Gruß
Thomas

Florian
12.08.2005, 17:16
Hallo Sebastian!
Schön, dass Du da bist, ich bin heute irgendwie nicht ganz so der Erklärbär, bekomme heute nichts hin! ;o)

izaseba
12.08.2005, 17:19
Leute, ihr habt da jemanden übersehen, der hier auchmal eine Frage gestellt hat.

Hallo Tekeli,
ich freue mich, daß Dir unser Spielchen hier gefällt, und das Du auch mitmachst!
Ja Du hast recht, ich habe den Asuro, macht Spaß das dingen.

Ich schaue mir den Plan so an von Asuro und würde mal so sagen , ohne was auszubauen würde ich den an die Linien Diode hängen, aber dann gegen VCC (+),
also ein pin von Piezo an PD6 und der andere an Plus, die Diode würde auch was glimmen, aber kaputtgehen dürfte eingentlich nichts .

Ich übernehme aber keine Verantwortung für defekten Asuro !!!

Ich hoffe, daß mich hier jemand bessert, wenn das nicht stimmen sollte.

Gruß Sebastian

Tekeli
12.08.2005, 17:24
Hallo Sebastian,

vielen Dank für Deinen Vorschlag!
Ich werde diesen gleich ausprobieren. :)

Best wishes

izaseba
12.08.2005, 17:25
@Florian, ich weiß wie schwer es ist jemandem etwas beizubringen,
schön daß Du Dich um den Thomas gekümmert hast, wie gesagt, ich habe es nicht so schön, wie Ihr und muß knechten :o
Aber in zwei Wochen drehen wir den Spieß um \:D/ , da hab ich Urlaub!

@Florian,
weißt Du jetzt, wie Du den TIMSK mit 2 Werten belegst?

wo sind wir jetzt stehengeblieben?
Wo war das Problem?

Ich habe zwar alle Beiträge nachgelesen, aber richtig schlau bin ich nicht geworden,
der Thomas hatte ein Problem beide Timer zu benutzen, oder war dort was mit Falschem Timing ???

Florian
12.08.2005, 17:25
Ups, 'tschuldigung Tekeli! *unschuldig guck*

@ Thomas: Kommst Du weiter?

@ Sebastian:
Thomas hatte erst TOIE0 aktiviert und dann nochmal mit TOIE2 darübergeschrieben und damit wieder TOIE0 deaktiviert! ;o)

izaseba
12.08.2005, 17:34
@Tekeli,
bitte schön, ich hoffe, daß es Dir gelingt!

Achso, dann lag ich wohl richtig mit dem "verodern" beider Bits.
Ich hoffe daß es jetzt endgültig klar ist was (1<<PD2) | (1<<PD3) bedeutet.

P.S. Es können auch mehr als 2 Bits sein (1<<PD2) | (1<<PD3) | (1<<PD3) ... usw

Vorteil -> man sieht auf den ersten Blick, was man da für Bits gesetzt hat

toeoe
12.08.2005, 17:41
Sebastian: In 2 Wochen hast du Urlaub? In 2 Wochen muss ich arbeiten :( *gg*

Das mit mehr als 2 Bits setzen hab ich dann heut auch kennengelernt, da ich beim Timer 2 3 Bits auf 1 setzen musste, damit ich einen Prescaller von 1024 habe.
Von weiterkommen kann nicht ganz die Rede sein, ich glaub ich änder den Code immer nur um, aber ändern tut sich nichts. Am Anfang hört man kurz ein Knacken von Summer, und dann isses auch schon wieder vorbei. Hier erstmal bis jetzt der Code:

.include "m8def.inc"

.equ time0_1 = 256-4 ;Damit wird der Timer2 vorgeladen - für 1KHz
.equ time0_2 = 256-1 ;Damit wird der Timer2 vorgeladen - für 4KHz
.equ time2 = 256-90 ;Damit wird der Timer0 vorgeladen
.equ Summer = PB2 ;Summer an B.2
.def tmp = r16 ;Mein Universallregister
.def statusreg = r17 ;Mein Statusregister
.def zaehler = r18 ;Mein Zählregister
.def statusSummer = r19 ;Mein Summerregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefZaehler ;Interruptvektor für Timer2 Überlauf, hier springt
;das Programm hin, wenn der Timer1 überläuft

.org OVF0addr ;Interruptvektor für Timer0 Überlauf, hier springt
rjmp pruefTon ;das Programm hin, wenn Timer0 überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten #RAM-Adresse
out SPL, tmp

sbi DDRB, Summer ;B.2 als Ausgang
sbi PortB, 2 ;B.2 am Anfang aus 1 stellen, damit Ton rauskommt

ldi statusSummer, 0b00000000 ;Das erste Bit (Bit0) auf 0 setzen, damit er weiß,
;welchen Ton er abspielen soll. (0 = 1KHz - 1 = 2KHz)

;Timer Register werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCCR2 ist für den Prescaller zuständig
ldi tmp, time2 ;Hier wird der Timer vorgeladen und zwar mit 256-90
out TCNT2, tmp

;Timer Register werden belegt, hier Timer0
ldi tmp, (1<<CS02) | (1<<CS00) ;Prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0_1 ;Hier wird der Timer vorgeladen und zwar mit 256-4
out TCNT0, tmp

ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 und Timer2 eingeschaltet
out TIMSK, tmp ;Register TISK ist dafür zuständig

sei ;Interrupts zulassen

loop:
cpi zaehler, 0b00010100 ;Wenn Zählregister != 20 ist
brne loop ;dann spring wieder zurück zu "loop:"

zeitum0:
clr zaehler ;Zählregister auf 0 setzen
sbrc statusSummer, 0 ;überspringe, wenn Bit0 = 0 ist
rjmp Ton2

Ton1:
ldi tmp, time0_1 ;wenn Bit0 = 0 ist, dann mach lad Frequenz 1KHz
out TCNT0, tmp
rjmp loop ;Wieder zurück zu "loop:"

Ton2:
ldi tmp, time0_2 ;wenn Bit0 = 1 ist, dann mach lad Frequenz 4KHz
out TCNT0, tmp
rjmp loop ;Wieder zurück zu "loop:"

pruefZaehler:
in statusreg, SREG ;SREG sichern
inc zaehler ;Zählregister um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgelaen und zwar mit 255-90
out TCNT2, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist

pruefTon:
in statusreg, SREG ;SREG sichern

sbrs statusSummer, 0 ;überspringe, wenn Bit0 = 1 ist
sbr statusSummer, 0 ;wenn Bit0 = 0 ist, dann auf 1 setzen

sbrc statusSummer, 0 ;überspringe, wenn Bit0 = 0 ist
cbr statusSummer, 0 ;wenn Bit0 = 1 ist, dann auf 0 setzen

out SREG, statusreg
reti

izaseba
12.08.2005, 18:06
Hallo,

Ich habe mir kurz Dein Programm angeschaut, dazu ein paar anmerkungen,

.equ time0_1 = 256-4 ;Damit wird der Timer2 vorgeladen - für 1KHz
.equ time0_2 = 256-1 ;Damit wird der Timer2 vorgeladen - für 4KHz

bist Du sicher, daß es stimmt?

ich hab selber noch nicht nachgerechnet, aber hmmm.

Den Timer 0 mußt Du in seiner Interruptroutine neu laden, und nicht im Hauptprogramm.

und pruefTon würde ich ganz langsam durchgehen, ob da nicht was falsch ist .

Gruß Sebastian

toeoe
12.08.2005, 18:12
HI,

die Werte müssten stimmen, das hab ich geprüft.
Hatte das Neuladen von Timer0 vorher auch in der Interruptroutine drinne, änder es ja ständig, langsam blick ich auch nicht mehr durch meinen Code durch.
Ich schau mir dann nochmal pruefTon an, obwohl ich das ja auch mittlerweile schon wieder geändert habe *g*

Gruß
Thomas

[edit]
Um euch auf dem Laufenden zu halten, hier mein aktuelle Code:

.include "m8def.inc"

.equ time0_1 = 256-4 ;Damit wird der Timer2 vorgeladen - für 1KHz
.equ time0_2 = 256-1 ;Damit wird der Timer2 vorgeladen - für 4KHz
.equ time2 = 256-90 ;Damit wird der Timer0 vorgeladen
.equ Summer = PB2 ;Summer an B.2
.def tmp = r16 ;Mein Universallregister
.def statusreg = r17 ;Mein Statusregister
.def zaehlerSek = r18 ;Mein Zählregister für die halbe Sekunde
.def statusSummer = r19 ;Mein Summerregister
.def zaehlerTon = r20 ;Mein Zählregister für den Ton

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefZaehler ;Interruptvektor für Timer2 Überlauf, hier springt
;das Programm hin, wenn der Timer1 überläuft

.org OVF0addr ;Interruptvektor für Timer0 Überlauf, hier springt
rjmp pruefTon ;das Programm hin, wenn Timer0 überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten #RAM-Adresse
out SPL, tmp

sbi DDRB, Summer ;B.2 als Ausgang
sbi PortB, 2 ;B.2 am Anfang aus 1 stellen, damit Ton rauskommt

ldi statusSummer, 0b00000000 ;Das erste Bit (Bit0) auf 0 setzen, damit er weiß,
;welchen Ton er abspielen soll. (0 = 1KHz - 1 = 2KHz)

;Timer Register werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCCR2 ist für den Prescaller zuständig
ldi tmp, time2 ;Hier wird der Timer vorgeladen und zwar mit 256-90
out TCNT2, tmp

;Timer Register werden belegt, hier Timer0
ldi tmp, (1<<CS02) | (1<<CS00) ;Prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0_1 ;Hier wird der Timer vorgeladen und zwar mit 256-4
out TCNT0, tmp

ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 und Timer2 eingeschaltet
out TIMSK, tmp ;Register TISK ist dafür zuständig

sei ;Interrupts zulassen

loop:
cpi zaehlerTon, 0b00000001 ;Wenn Zählregister für Ton = 1 ist
breq pruefFrequenz ;dann spring zu "pruefTon:"
cpi zaehlerSek, 0b00010100 ;Wenn Zählregister != 20 ist
brne loop ;dann spring wieder zurück zu "loop:"

zeitum0:
clr zaehlerSek ;Zählregister auf 0 setzen
rjmp loop

pruefFrequenz:
sbrs statusSummer, 0 ;überspringe, wenn Bit0 = 1 ist
sbr statusSummer, 0 ;wenn Bit0 = 0 ist, dann auf 1 setzen
sbrc statusSummer, 0 ;überspringen, wenn Bit0 = 0 ist
cbr statusSummer, 0 ;wenn Bit0 = 1 ist, dann auf 0 setzen
rjmp loop

pruefZaehler:
in statusreg, SREG ;SREG sichern
inc zaehlerSek ;Zählregister um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgelaen und zwar mit 255-90
out TCNT2, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist

pruefTon:
in statusreg, SREG ;SREG sichern
inc zaehlerTon ;Zählregister um 1 erhöhen

sbrs statusSummer, 0 ;überspringe, wenn Bit0 = 1 ist
ldi tmp, time0_2 ;wenn Bit0 = 0 ist, dann Frequenz 2 (4KHz) laden
sbrc statusSummer, 0 ;überspring, wenn Bit0 = 0 ist
ldi tmp, time0_1 ;wenn Bit0 = 1 ist, dann Frequenz 1 (1KHz) laden
out TCNT0, tmp

out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist
Hab nun für Timer0 auch einen Zähler eingebaut, denk mal, den brauch ich. in pruefTon wird der Timer immer wieder neugeladen. Tjo, und den Rest find ich eigentlich logisch. Ich denk mal, der Fehler muss in pruefTon liegen, da er ja nur ein kurzes Knacken von sich gibt.

izaseba
12.08.2005, 18:33
Thomas,
so werden wir nicht weiterkommen.

Du mußt Dir erstmal ein Zettel nehmen, und darauf fängt Deine Arbeit an,
mann kann sich dort den zeitlichen Ablauf schön malen, wie das aussehen sollte,
dann schreib Dir nur einen Interrupt für den Ton.
darin dürfte nach meinem Kopf nur eins stehen, und zwar das neuladen des Timers!
Prufe aber darin deinen Status Summer, und jenachdem welchen Wert der hat lädst Du anderen Wert ein.
Und im anderem Interrupt -> dem für die Sekunde den inc rein, und im Hauptprogramm
nur prüfen ob sekunde um ist und jenachdem den Status Summer umschalten, so müßte es gehen.

izaseba
12.08.2005, 18:46
Außerdem stell ich mir die Frage, an welcher stelle Du den Pin umschaltest, wo Dein Summer dranhängt?


sbi PortB, 2

hier setzt Du ihn auf high,und an welcher stelle geht er auf LOW?
irgendwie muß Du ihn in 4khz oder 1kHz Takt umschalten,
oder habe ich da was auf den Augen ?????

toeoe
12.08.2005, 18:53
Ich setz ihn gar nicht auf LOW. Er soll von Anfang an den Ton ausgeben, das passiert bei mir, wenn das Bit 1 ist.
Ich mal mir grad nen Schema, setzte es grad noch in Paint um und poste es dann hier.

toeoe
12.08.2005, 19:01
So, hier ist mal mein Schema, so müsste es doch stimme, oder?

izaseba
12.08.2005, 19:05
Ich setz ihn gar nicht auf LOW

Und das ist schon ein großer Fehler, so wird er Dir nie ein Ton rausgeben....
Wenn Du ihn einmal nur auf High setzt dann ist es so als ob Du ihn an eine Batterie anschließt und Fertig.
Deine Frequenz ist gleich null.
Dein Programm wird schon richtig funktionieren, also den Timer 0 mit verschiedenen Werten beladen, mehr auch nicht. Du mußt in dem Timer schon den Zustand vum PB2 ändern, damit er Piept sagt.
Im moment sieht es am PB2 so aus : 1111111111111111111111111111111
Es soll aber so aussehen :1010101010101010101010101010101

Jetzt nicht böse sein!!
Wieviel Ahnung hast Du von Elektronik / Elektrotechnik ?
Ich habe das Glück, daß ich aus der Branche komme, und irgendwann sowas in der Schule hatte,
das muß aber nicht bei jedem der Fall sein!

Florian
12.08.2005, 19:11
Kurze Zwischenmeldung, wir steigen jetzt mit dem 182. Post in die Top-Ten-Thread-Statistik ein!
https://www.roboternetz.de/phpBB2/statistics.php

toeoe
12.08.2005, 19:11
In der Schule hatte ich nie was mit Elektronik zu tun, außer halt in Physik. Aber dann auch nicht sowas, sondern nur die Standardsachen wir Reihen- und Paralellschltung, sorry.

@Florian: Hehe, net schlecht ;) Unser Thread is eben was besonderes *g*

Ich glaub es ist am besten, wenn ich nochmal von vorn anfang, denn ich blick echt nimmer durch den Code durch, durch das 1000x geändere.

izaseba
12.08.2005, 19:30
durch das 1000x geändere.
eben

Je mehr Du geändert hast um so schlechter wurde das Program.

hier nochmal die Grundlagen:
Und ganz langsam, fang so an:
1.
Ein Timer, der mit 4 Khz arbeitet schaltet in der Interruptroutine den PB2 zwischen 1 und 0 um.
In dem Interrupt PB2 umschalten und Timer neu laden
Somit müßtest Du ein Piepen hören!

2. Zweiten Timer dazubasteln, der mit hilfe einer HilfsVariable 1 Sekundentakt erzeugt.
In diesem Interrupt nur die Hilfsvariable hochzählen und Timer neu laden.
Im Hauptprogramm Hilfsvariable überprüfen, und wenn Wert erreicht wurde leeren
und eine Diode umschalten.
Hier müßte es immernoch piepen, und Die Diode müßte blinken.
Somit siehst Du daß beide Timer funktionieren.

Wenn Du das hast, dann machen wir weiter!

izaseba
12.08.2005, 19:37
@ Florian, seit wann heißt Dieser Thread "Tutorial für alle Assembler Anfänger" ?

@Thomas, genauso sollte man sich das immer Zeichnen

Florian
12.08.2005, 20:02
@ Florian, seit wann heißt Dieser Thread "Tutorial für alle Assembler Anfänger"?Seit eben! ;o)
Ich dachte, es wäre etwas besser vom Titel her, denn um das eigentliche Titelthema geht es ja im Grunde genommen nicht mehr!
War das nicht ok?

izaseba
12.08.2005, 20:06
War das nicht ok?

Ich denke schon, worum ging es sich eigentlich?
Achja eine LED ein und auszuschalten hehe

Gruß Sebastian

Florian
12.08.2005, 20:17
Ich finde da haben wir schon einiges geschafft!

toeoe
12.08.2005, 20:20
So, wenn ich das Umschalten von PB2 in die Interrouptroutine mache, hör ich kein Piepen mehr. Nur wenn ich das Umschalten so mache, wie wir das bei dem "Lichtschalter" gemacht haben.
Hier der Code:

.include "m8def.inc"

.equ time0_1 = 256-2 ;Damit wird der Timer vorgeladen
.equ Summer = PB2 ;Summer an B.2
.def tmp = r16 ;Mein Universallregister
.def statusreg = r17 ;Mein Statusregister
;.def zaehler = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp pruefZaehler ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig
ldi tmp, time0_1 ;Hier wird der Timmer vorgelaen und zwar mit 255-90
out TCNT0, tmp ;Er läuft 90 mal durch, bevor ein Interrupt auftritt
sei ;Interrupts zulassen

loop:
rjmp loop ;immer wieder zu "loop:" springen

pruefZaehler:
in statusreg, SREG ;SREG sichern

sbis PINB, Summer ;überspringe, wenn B.2 = 0 ist
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann auf 0 setzen
sbic PINB, Summer ;überspringe, wenn B.2 = 1 ist
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

ldi tmp, time0_1 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist
Finds irgendwie komisch :/

izaseba
12.08.2005, 20:28
sbis PINB, Summer ;überspringe, wenn B.2 = 0 ist

Ist Das richtig?

toeoe
12.08.2005, 20:32
Ach shit, sry :cry:
*selbst ne Ohrfeige geb* So, nu bin ich wieder da. Also Ton kommt erstmal. Nun werd ich mal eine LED am anderen Pin mit einem weiteren Timer zum blinken bringen.

izaseba
12.08.2005, 20:34
sbis PINB, Summer ;überspringe, wenn B.2 = 0 ist
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann auf 0 setzen
sbic PINB, Summer ;überspringe, wenn B.2 = 1 ist
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

ldi tmp, time0_1 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist

schaue mal hier:

sbis PINB, Summer
rjmp umschalten1
cbi PORTB,Summer
rjmp umschalten2
umschalten1:
sbi PORTB,Sumer
umschalten2:
ldi tmp, time0_1
out TCNT0, tmp
out SREG, statusreg
reti

Ich finde das besser...

izaseba
12.08.2005, 20:38
Thomas, nicht weinen!

Du darfst Dich nicht unterdruck setzen!
Immer cool bleiben, und langsam,
keiner will hier in einer Woche einen Profi aus Dir machen!
Leute studieren um das zu verstehen, wir machen das nur, weil es uns
Spaß macht, und es soll auch so bleiben!!!

So, Nase Putzen und Tränen wegwischen!!!



:lol:

toeoe
12.08.2005, 21:06
Dein Code gefällt mir auch besser :)
Mir fehlt einfach noch die Routine, in Assembler zu programmieren. Also die ganze Logik dahinter, aber das kommt ja mit der Zeit dann.

izaseba
12.08.2005, 21:21
Mir fehlt einfach noch die Routine, in Assembler zu programmieren

Und die bekommst Du, nur wenn Du programmierst!

toeoe
12.08.2005, 22:20
\:D/
Habs nu hinbekommen. Also die "Teilaufgabe". Es piept und die LED blinkt im Sekundentakt:

.include "m8def.inc"

.equ time0_1 = 256-2 ;Damit wird der Timer0 vorgeladen
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen
.equ Summer = PB2 ;Summer an B.2
.equ LED = PB5 ;LED an B.5
.def tmp = r16 ;Mein Universallregister
.def statusreg = r17 ;Mein Statusregister
.def zaehlerLED = r18 ;Mein Zählregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp timerLED

.org OVF0addr
rjmp timerSummer ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, Summer ;B.2 als Ausgang
sbi DDRB, LED ;B.5 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen
sbi PORTB, LED ;B.5 auf HIGH stellen

ldi zaehlerLED, 0b00000000 ;Zähler auf 0 setzen

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0_1 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCRR2 ist für den Prescaller zuständig
ldi tmp, time2 ;Hier wird der Timer vorgeladen
out TCNT2, tmp

ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

sei ;Interrupts zulassen

loop:
cpi zaehlerLED, 0b00101000 ;Wenn Zählerregister != 40 ist
breq pruefLED ;dann zu "pruefLED:" springen
rjmp loop ;immer wieder zu "loop:" springen

pruefLED:
clr zaehlerLED ;Zählerregister auf 0 setzen
sbis PORTB, LED ;überspring, wenn B.5 = 1 ist
rjmp umschaltenLED ;wenn B.5 = 0 ist, dann spring zu "umschaltenLED:"
cbi PORTB, LED ;wenn B.5 = 1 ist, dann B.2 auf 0 setzen
rjmp loop ;wieder zurück zu "loop:"

umschaltenLED:
sbi PORTB, LED ;wenn B.5 = 0 ist, dann auf 1 setzen
rjmp loop ;wieder zurück zu "loop:"

timerSummer:
in statusreg, SREG ;SREG sichern

sbis PINB, Summer ;überspringe, wenn B.2 = 1 ist
rjmp umschaltenSummer1 ;wenn B.2 = 0 ist, dann spring zu "umschalten1:"
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann B.2 auf 0 setzen
rjmp umschaltenSummer2 ;zu "umschalten2:" springen

umschaltenSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

umschaltenSummer2:
ldi tmp, time0_1 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist

timerLED:
in statusreg, SREG ;SREG sichern
inc zaehlerLED ;Zählerregister um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer2 vorgeladen
out TCNT2, tmp
out SREG, statusreg ;SREG wiederholen
reti
Nu werd ich erstmal was essen :)

Florian
12.08.2005, 22:41
Herzlichen Glückwunsch!

izaseba
12.08.2005, 22:54
Na bravo, es sieht sehr ordentlich aus viel besser hätte man das nicht machen können.

Jetzt hast Du 2 Timer die völlig unabhängig voneinander laufen, und vor allem 2 Interruptsroutinen!

Und Du warst schon fast fertig mit den Nerven.
Aber eins kannst Du mir glauben, es ging mir mal auch nicht besser.

Und vor allem siehst Du jetzt daß du den PB2 sehr schnell hin und her schalten mußt,
und nicht wie Du mal hattest PB2 auf 1 und fertig.

Jetzt gilt es die beiden Timer irgendwie miteinander zu Verknüpfen !

Da wir jetzt ganz kleine Schritte gehen wollen, benutzen wir die LED um zu schauen, ob Sekunde um ist oder nicht.

als erstes weise am Anfang die andere Frequenz zu, wie war das nochmal?
.equ Timer0_2 = 256 -4 ? kannst mich als faul bezeichnen, aber ich habe keine Lust jetzt nachzuschauen.

als nächstes gehst Du in Deine timerSummer Routine und zwar zum Label timerSummer2:

dort schauen wir erstmal nach ob die LED an ist:


sbis PORTB,LED
rjmp timerSummer3 ;wenn aus gehen wir nach timerSummer3
ldi tmp, time0_1 ;Hier wird der Timer für 1KHZ geladen???
out TCNT0, tmp
rjmp timerSummer4
timerSummer3:
ldi tmp, time0_2 ;Hier wird der Timer für 4KHZ geladen???
out TCNT0, tmp
timerSummer4:
out SREG, statusreg ;SREG wiederholen
reti


So ich habe es nicht ausprobiert, es müßte aber gehen (hoffe ich)

Einfach oder?
Ich hoffe, Du kannst nachvollziehen, warum wir gerade an dieser Stelle nachschauen,
ob die Sekunde um ist.

Probiere es so aus, ich bin selber gespannt.

Gruß Sebastian

toeoe
12.08.2005, 23:12
Das funktioniert ja wunderbar :) Und das beste, hab alles verstanden :)
Hab den Timer2 (für die LED) mal auf 0,5 Sekunden gestellt, nun hört sich das an wie eine Polizeisirene *gg*

Ich hoffe, Du kannst nachvollziehen, warum wir gerade an dieser Stelle nachschauen,
ob die Sekunde um ist.
Ich kann es mir denken. Denn bei timerSummer2 sind die letzten Schritte, bis der Timer vom Summer wieder von vorn anfängt zu zählen. Sonst wär es denk ich nicht genau synchron mit der LED, oder?

Gruß
Thomas

[edit]
Irgendwie nerven die Töne meine Freundin *gg* Musste deshalb leider auf niedrigere Frequenzen umsteigen *gg* :D
Nun hab ich nen Polizeitauto *freu wie ein kleines Kind* \:D/ *g*

Tekeli
12.08.2005, 23:21
Nacht,

die Sierene habe ich jetzt auch hingekriegt.
Nun habe ich einen Polizei-Asuro:)

Musste den Summer aber gegen GND anschliessen. :)

Anbei meine Version der Lösung.
Es macht echt Spaß mit dem Assembler zu programmieren!

Best wishes

toeoe
12.08.2005, 23:24
Na dann mal von mir Glückwunsch :)

Florian
12.08.2005, 23:26
Herzlichen Glückwunsch ihr beiden!
Ihr seit nun nicht mehr nur Anfänger, sondern schon fortgeschrittene Anfänger! *lol*
Nein, ihr könnt euch jetzt glaube ich mit ein bischen mehr Routine und Übung schon fast Fortgeschrittene schimpfen! ;o)

Macht weiter so!
Es macht echt Spaß mit dem Assembler zu programmieren!Sagen wir doch schon lange, aber irgendwie denken viele, dass nur "Hochsprachen" Spass machen können! ;o)

izaseba
12.08.2005, 23:26
Irgendwie nerven die Töne meine Freundin
Das kannst Du laut sagen, ich habe vor kurzem einen Timer für meine Frau gebaut,
naja Du weißt schon, für Kuchen zu Backen z.B 0-90 Minuten, man könnte natürlich
bei Real irgendwas Fertiges kaufen für 4,99 , aber nein, ich muß basteln.
Da habe ich auch so ein Piezo verwendet, wenn die Zeit um war und habe Ihn auf
etwa 4kHz eingestellt.
O weh ich durfte mir war anhören, muße auch tiefer gehen, so bei 800 Hz war sie zufrieden.

Ich glaube Frauen können solche Töne nicht vertragen, so wie Katzen, naja manchmal benehmen sie sich auch so
:lol:

Ich freue mich ein lächeln auf Deinem Gesicht zu sehen!

Und warum vergleichen wir gerade im Interrupt vom summer?
Ganz einfach, hier wird der Timer neugeladen, also hier bestimmst Du welche Frequenz Du hast, und wenn Du sie verändern willst, mußt Du auch genau dort eingreifen!

izaseba
12.08.2005, 23:35
Wie ich sehe haben wir noch einen Glücklichen hier (o man warum muß ich immer so langsam Tippen)
@Tekeli,
ging das nicht gegen VCC ? hmmm, na egal, aber die LED kannst Du trotzdem benutzen, oder?
Der Piezo müßte ziemlich hochohmig sein, so daß er da nicht stört (Theorie)

Mach mal ein Foto und sende das den Asuro Freunden auf "Roboter Bausätze" vielleicht finden sie das auch interessant...

toeoe
12.08.2005, 23:45
@Sebastian: Mein Freundin meint, dass Männer die Frequenz nicht stört, weil sie schlecht hören, so wie die meisten Fliegen, manchmal sind sie auch ebenso lästig ;)

Ich glaub, die will Krieg *Totenlied auffer Gitarre spiel :-({|= *gg*

So, hier nun der fertige Quellcode ohne LED, so wies "verlangt" war ;)

.include "m8def.inc"

.equ time0_1 = 256-20 ;Damit wird der Timer0 vorgeladen
.equ time0_2 = 256-30
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen
.equ Summer = PB2 ;Summer an B.2
.def tmp = r16 ;Mein Universallregister
.def statusreg = r17 ;Mein Statusregister
.def zaehlerSek = r18
.def statusSummer = r19 ;Mein Status-Summer-Register

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp timerSek

.org OVF0addr
rjmp timerSummer ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen
ldi statusSummer, 0b00000000 ;statusSummer auf 0 setzen
ldi zaehlerSek, 0b00000000 ;Zähler auf 0 setzen

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0_1 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCRR2 ist für den Prescaller zuständig
ldi tmp, time2 ;Hier wird der Timer vorgeladen
out TCNT2, tmp

ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

sei ;Interrupts zulassen

loop:
cpi zaehlerSek, 0b00010100 ;Wenn Zählerregister != 40 ist
breq pruefSek ;dann zu "pruefSek:" springen
rjmp loop ;immer wieder zu "loop:" springen

pruefSek:
clr zaehlerSek ;Zählerregister auf 0 setzen
sbrs statusSummer, 0 ;überspringe, wenn PIN 0 = 1 ist
rjmp umschalten ;wenn PIN 0 = 0 ist, dann springe zu "umschalten:"
ldi statusSummer, 0b00000000;wenn PIN 0 = 1 ist, dann PIN 0 auf 0 setzen
rjmp loop ;wieder zurück zu loop

umschalten:
ldi statusSummer, 0b00000001;wenn PIN 0 = 0 ist, dann auf 1 setzen
rjmp loop ;wieder zurück zu "loop:"

timerSummer:
in statusreg, SREG ;SREG sichern

sbis PINB, Summer ;überspringe, wenn B.2 = 1 ist
rjmp timerSummer1 ;wenn B.2 = 0 ist, dann spring zu "umschalten1:"
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann B.2 auf 0 setzen
rjmp timerSummer2 ;zu "umschalten2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

timerSummer2:
sbrs statusSummer, 0 ;überspringe, wenn PIN 0 = 1 ist
rjmp timerSummer3 ;wenn PIN 0 = 0 ist, dann springe zu "timerSummer3:"
ldi tmp, time0_1 ;wenn B.5 = 1 ist, dann 1 KHz-Timer laden
out TCNT0, tmp
rjmp timerSummer4 ;zu "timerSummer4:" springen

timerSummer3:
ldi tmp, time0_2 ;wenn B.5 = 0 ist, dann 4KHz-Timer laden
out TCNT0, tmp

timerSummer4:
out SREG, statusreg ;SREG wiederholen
reti ;wieder zurück, wo du hergekommen bist

timerSek:
in statusreg, SREG ;SREG sichern
inc zaehlerSek ;Zählerregister um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer2 vorgeladen
out TCNT2, tmp
out SREG, statusreg ;SREG wiederholen
reti

Gruß
The Happy Thomas :-b

izaseba
13.08.2005, 00:00
Ne,ne ich führe keinen Krieg gegen Frauen !!!!
Sie benutzen manchmal Waffen, wo wir arme schlucker nicht gegen ankommen !
Also schön den Ball flachhalten.

Das ohne LED hast Du auch gut gelöst, die Version mit war ja nur ein Übergang...

@Tekeli,
Du bist aber kein blutiger Anfänger ?
Hab mir Dein Code angeschaut, bin echt begeistert !

Ich find das gut daß Ihr zu zweit seid, Ihr könnt Euch gegenseitig helfen,
wenn was unklar ist.

Thomas, ich hoffe, Du hast Dir die Version von Tekli runtergeladen,
dort kannst Du sehen, daß es wie immer mehrere Wege gibt ein Problem zu lösen,
nur bedenke, der Asuro hat 8 Mhz, also hat er die Zeiten etwas anders.

Wenn Ihr Lust habt, haben wir beide mit Florian eine sehr schöne Aufgabe vorbereitet.
Ich war begeistert, was sich der Florian alles so einfallenlassen hat.

Nur um das anzupacken, müßten wir noch mehrere Sachen ansprechen, die erste wäre
was macht man, wenn man eine Zahl darstellen will, die größer 255 ist z.B 1000 ?

Irgendwelche Ideen ?

Florian
13.08.2005, 00:01
Kleine Nachricht, bevor ich in's Bettchen gehe und Bubu mache:
Wir sind jetzt in der Top-Ten-Statistik auf Platz 8! *angeb* ;o)

Und zur Ernüchterung, damit alle schlafen können:
Ihr solltet mal unbedingt an eurem Codedesign arbeiten!
Hier, lest das mal! (http://www.elektronik-projekt.de/include.php?path=content/articles.php&contentid=129&PHPKITSID=45538b15ce0e87f53fa3812a595a095e)
Sonst funktioniert der Code zwar, aber zwei Tage später wisst ihr nicht mehr was ihr da verbrochen habt! *g*


Ps:
Das Lied vom Tod auf einer Gitarre zu spielen ist unüblich, außerdem ist das eine Geige, auf der Du da rumgeigst! ;o)

toeoe
13.08.2005, 00:05
Den Code von Tekeli hab ich mir runtergeladen und auch studiert. Wurde erstmal umgehaun von den rießigen Kommentaren oben *g*
Rechnen ist immer gut *g*
Bis jetzt haben wir ja immer nur mit 8 Bit gearbeite, und diese sind bei 255 (11111111) zu Ende.
Mit 16Bit bekommt man Zahlen bis 65025 (11111111 11111111). Also muss man irgendwie zwei 8 Bit Register miteinander verbinden.

Jaja, ich gebs ja zu, hab mich da schon ein wenig drüber informiert, interessiert mich halt, ist ja auch nicht verboten ;)

[edit] Dann eben Geige *g*

@Florian: Das mit den Kommentaren mach ich ja, also einrücken und alle Kommentare in der gleichen Spalte. Sieht man nur hier im Forum nicht, aber im AVR Studio isses wunderbar.
Ok, den Code könnt man noch mehr einrücken, wenn man Blöcke hat, die zusammengehören.

izaseba
13.08.2005, 00:26
ist ja auch nicht verboten

Thomas, es ist sogar angebracht selber zu forschen, ich hab Dir den link zum Tutorial
gegeben, damit Du auch ein Nachschlagewerk hast, und vor allem, daß Du Dir das alles da reinziehst!

Ich habe nur mit Dieser Seite und mit Dattenblättern gelernt.

Also Selbststudium ist wirklich das a und o.

Aber Du hast recht, man braucht zwei Register, und wenn die nicht reichen dann nimmst Du halt drei, oder 4 oder 8.
Zugegeben, mit solchen Zahlen ist es nicht einfach zu arbeiten, aber wenn man sie braucht
ist es kein Problem.



Wurde erstmal umgehaun von den rießigen Kommentaren oben

Spätestens in einem Monat, wenn Du Dir das anschaust, wirst Du froh sein,
solche Komentare gemacht zu haben.

Das Problem an Assembler ist es, daß man verdammt viel Code hat, und wenn man
noch Programme von anderen Personen lesen und verstehen will, wo wenig oder keine Komentare sind, bekommt man graue Haare.


Dann eben Geige *g*

Warum Florian soviel Wert drauf legt wirst Du Morgen erfahren.
Ich muß Morgen leider arbeiten ](*,) ](*,) ](*,) womit ich mir das Spielchen leider nicht
angucken kann, aber ich hoffe der Florian hat mehr Zeit und stellt Dir das vor.
Bis dahin kannst Du dich im Tutorial über Zahlen jenseits der 255 marke , den Befehl lpm und die Zeigerregister informieren, glaube mir, es ist wirklich einfach beschrieben.
Ich werde mich jetzt verabschieden, und Euch allen eine gute Nacht wünschen.

Ich freue mich wirklich , daß wir das heute mit der Sirene hinbekommen haben.

Gruß Sebastian

toeoe
13.08.2005, 00:35
Ok, schade, dass du morgen arbeiten musst. Hoffentlich nicht so lange, wie in der Woche?
Najo, also dann bis morgen.

Gruß und gute Nacht
Thomas

Florian
13.08.2005, 08:28
Moin, moin!
Schade, dass Sebastian heute arbeiten muss! *schluchts*
Ich weiß leider nicht, wie viel Zeit ich heute habe bzw. wie lange ich an den PC kann, 5 Wochen habe ich nämlich noch meine Schwester als Anwärterin auf den PC! ;o)
Naja, ich werde versuchen das Ganze einzurichten!


@Florian: Das mit den Kommentaren mach ich ja, also einrücken und alle Kommentare in der gleichen Spalte. Sieht man nur hier im Forum nicht, aber im AVR Studio isses wunderbar.
Ok, den Code könnt man noch mehr einrücken, wenn man Blöcke hat, die zusammengehören.Achso, nagut, ich gebe mich geschlagen! ;o)
Ich muss zugeben, dass ich noch nicht so oft Code gepostet habe! *lol*


Ich habe nur mit Dieser Seite und mit Dattenblättern gelernt.Ich habe angefangen mit www.mikrocontroller.net, danach habe ich mit www.avr-asm-tutorial.net weiter gemacht und zum Schluss habe ich noch die Tutorials von Gerhard Paulus durchgearbeitet.


Warum Florian soviel Wert drauf legt wirst Du Morgen erfahren.
Ich muß Morgen leider arbeiten womit ich mir das Spielchen leider nicht
angucken kann, aber ich hoffe der Florian hat mehr Zeit und stellt Dir das vor.Ich werde versuchen es zeitlich einzurichten!


Bis dahin kannst Du dich im Tutorial über Zahlen jenseits der 255 marke , den Befehl lpm und die Zeigerregister informieren, glaube mir, es ist wirklich einfach beschrieben.
Und vor allem auch die Datenspeicher-Direktiven DW. und DB. und wo die Unterschiede liegen!
Ich persönlich liebe DB. und lpm, es sind meine Lieblingsthemen! ;o)


Ich freue mich wirklich , daß wir das heute mit der Sirene hinbekommen haben.Ich freue mich auch, aber ich bin auch gespannt, wie lange wir für das nächste Kapitel brauchen werden!?

Florian
13.08.2005, 10:07
So, seit ihr beiden, Thomas und Tekeli, da? ;o)
Dann kann es ja theoretisch los gehen mit der Aufgabe! *freu*

toeoe
13.08.2005, 10:08
Ist kein Problem wegen der Zeit. Einfach schreiben, wenn man Zeit hat ;) Hab mir gestern schon das mit den Zahlen durchgelesen und ich glaub, dass ich mir schon irgendwie vorstellen kann, wie man sie verbindet.
Bin gespannt, was sonst noch auf mich zukommt *g*

Muss jetzt eh erstmal einkaufen gehen, schau aber gleich wieder rein, wenn ich da bin *suchti* :/ *g*

Gruß
Thomas

[edit]
Da bin ich, pünktlich zum Unterrichtsbeginn ;) Aber wie gesagt, bin dann erstmal kurz wech.

Florian
13.08.2005, 10:14
Gut, dann bis gleich!
Melde Dich dann bitte, wenn es losgehen kann! ;o)

Ist Tekeli auch da?

toeoe
13.08.2005, 10:15
Als online wird er ja angezeigt ;)
Meinte er nicht, er macht im Hintergrund mit? Najo, kannst ja schon die Aufgabe posten, dann kann ich mir im Auto schon Gedanken machen *g*

[edit]
So, nun wech, ich meld mich dann.

Tekeli
13.08.2005, 10:55
Moin moin,

bin ganz Ohr :)

Florian
13.08.2005, 10:58
Hier kommt die Aufgabe!
Seit bitte nicht zu geschockt, es ist nicht ganz leicht, aber Sebastian und ich werden euch langsam darauf hinzuführen!

Wir haben als nächstes vor, ein Soundmodul zu programmieren.
Wir werden einfach eine C-Dur-Tonleiter *für die Musiker unter uns* einmal rauf und einmal runter spielen.
Die Frequenzen findet ihr hier:
http://www.pianotip.de/frequenz.htm
Wir werden ersteinmal bei dem c' mit 261,63 Hz anfangen, über den Kammerton a' 440,00 Hz bis hin zum c'' 523,25 Hz.
Die Halbtonschritte lassen wir im ersten Teil weg, ich will euch ja nicht mit der Musiktheorie überfordern! ;o)

Damit wir nun nicht alle Töne einzeln mit großem Aufwand programmieren müssen, werden wir die Werte für die Töne im Speicher des AVR's ablegen.
Das machen mir mit den so genannten Direktiven .DW oder .DB!
Ich persönlich mache es gerne, dass ich die Daten ganz an's Ende des Codes lege.
Das sieht dann folgendermaßen aus:

.include "m8def.inc"
.
.
.
main:
...

subroutienen:
...

daten1:
.dw 65535 , 0 ; zwei sogenannte Words

daten2:
.db "Hallo!" , 10 , 13 ; immer eine gerade Anzahl an Bytes

Wenn man nun die Daten auslesen möchte, muss man das mit Hilfe des Z-Registers und des Befehls lpm machen.
Lest euch das bitte durch: http://www.avr-asm-tutorial.net/avr_de/beginner/index.html

Mit Hilfe des Z-Registers, welches ein 16-Bit-Register ist, legen wir die Position des Zeigers im Speicher fest.
An der Position wird dann mit lpm das Byte ausgelesen und in das Register 0 geschoben. Das können wir dann auslesen.

Schreibt jetzt bitte einen Code, der mit Hilfe des AVR-Speichers in r16 (temp) den Text "Hallo!" ausgibt. Ganz wichtig ist immer, dass ihr eine gerade Anzahl an Bytes benutzt, das hängt mit der Speicherstruktur zusammen.

So weit, so mehr oder weniger gut! ;o)
ich hoffe ihr habt wenigstens etwas verstanden, was ich von euch verlange! *g -> schäm für diesen unverständlichen Post*

Wenn ihr Fragen habt, dann fragt!
Viel Erfolg!

toeoe
13.08.2005, 11:37
Also ich hab jetzt mal geschaut, aber irgendwie ist das ziemlich kompliziert geschrieben. Einmal benutzt er da Y und einmal Z. Muss ich da nun auch beide Register mit der "Adresse" belegen?
Vor allem seh ich da gar nicht durch, was ich da nun zum Lösen der Aufgabe brauch und wie sollen wir den Text denn ausgeben? Können wir den Text nicht einfach irgendwie in r16 kopieren?
Sorry, aber wahrscheinlich bin ich echt zu doof dafür *g*
Und dann multipliziert er im Tutorial einmal die Adresse mit 2, ja, weils ein Wort ist, also 2 Bytes. Versteh ich aber leider nicht, warum man das dann x2 nehmen muss.
Ich hatte bis jetzt immer Bücher zum lernen, aber da gibts ja leider keine guten...

Tekeli
13.08.2005, 11:51
So wie ich das verstanden habe, ist der Programmspeicher WORD-weise organisiert. Also unter einer Addresse im Programmspeicher findet man 2 Byte. Also z.B.

$3400 "0011 0100" "1000 0001"

Byte1 = "0011 0100"
Byte2 = "1000 0001"

Deswegen beim Zugriff sollte man schon sagen, was man will. Will man Byte1 oder Byte2.

Ist das richtig so?
[EDIT]
Und da man mit dem Programmspeicher arbeitet, benutzt man Z-Register.

@Masters
Wo sollen wir den "Hello!" ausgeben? Oder sollen wir eine Schleife machen, die Daten aus dem Programmspeicher byteweise einliest, und in der r16 schreibt?

toeoe
13.08.2005, 11:56
Ja, das denk ich auch. Im Tutorial steht folgender Code:

.EQU Adresse = RAMEND ; In RAMEND steht die höchste SRAM-Adresse des Chips
LDI YH,HIGH(Adresse)
LDI YL,LOW(Adresse)
Und weiter unten dann das hier:

LDI ZH,HIGH(2*Adresse)
LDI ZL,LOW(2*Adresse)
LPM
Aber das versteh ich nicht, brauch ich denn da nun beide Register, kann ich mir aber nicht vorstellen, reicht doch, wenn ich nur ein Registerpaar nehme, oder?
Aber wie gesagt, versteh nicht, warum der mit 2 multiplizieren muss.

Florian
13.08.2005, 12:05
Hallo ihr Beiden!
Ich bin mir bewusst, dass es ziemlich verwirrend ist!

Sorry, aber wahrscheinlich bin ich echt zu doof dafür *g*Das glaube ich weniger! ;o)
Ihr braucht euch erstmal nur mit dem Zählerregister Z, also ZL und ZH beschäftigen, die anderen Anwendungen werdet ihr später kennenlernen!

Empfehlungen zur Registerwahl
1. Register immer mit der .DEF-Anweisung festlegen, nie direkt verwenden.
2. Werden Pointer-Register für RAM u.a. benötigt, R26 bis R31 dafür reservieren.
3. 16-Bit-Zähler oder ähnliches realisiert man am besten in R24/R25.
4. Soll aus dem Programmspeicher gelesen werden, Z (R30/31) und R0 dafür reservieren. (WICHTIG!)
5. Werden oft konstante Werte oder Zugriffe auf einzelne Bits in einem Register verwendet, dann die Register R16 bis R23 dafür vorzugsweise reservieren.
6. Für alle anderen Anwendungsfälle vorzugsweise R1 bis R15 verwenden.

So wie ich das verstanden habe, ist der Programmspeicher WORD-weise organisiert. Also unter einer Addresse im Programmspeicher findet man 2 Byte.Richtig!

Ich braucht euch da jetzt nicht große Angst machen, nehmt einfach mal zwei und fertig! ;o)
Also ladet ihr die Adresse der Daten so in das Z-Register:

ldi ZL , LOW (datenlabel * 2) ; das kennt ihr ja schon mit Ramend!
ldi ZH , HIGH (datenlabel * 2) ; wie oben

Jetzt sollt ihr mit lpm die Daten dazu in r0 laden und das dann in r16 rüberschieben.
Danach sollt ihr das Z-Register um 1 erhöhen, um das nächste Byte zu bekommen, somit hätte sich die Frage mit den Word's und Byte's erledigt! ;o)

Das ist alles, was ich von euch sehen will! *lol*
Ich hoffe, ich war jetzt verständlicher! *g*

Florian
13.08.2005, 12:16
Irgendwo scheint ja etwas nicht klar zu sein!
Wenn ihr möchtet zeige ich euch mal einen fertigen Code und erkläre euch das, ok?
Oder wollt ihr es jetzt selber herausfuseln!? *lol*

Tekeli
13.08.2005, 12:18
Wir lesen noch ein klein bißchen! :)
Gibt uns eine Chance!
http://www.mikrocontroller.net/tutorial/memory

toeoe
13.08.2005, 12:20
Selber herrausfinden bringt wohl mehr, aber ich komm da ja nun gar nicht klar. Hier mal mein Code:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

ldi ZH, HIGH(daten2 * 2)
ldi ZL, LOW(daten2 * 2)
lpm

sei ;Interrupts zulassen

loop:
adiw ZL,1
lpm

ST Z,tmp

rjmp loop ;immer wieder zu "loop:" springen

daten1:
.dw 65535 , 0 ;zwei Words

daten2:
.db "Hallo!" ;immer eine gerade Anzahl an Bytes
Wie man sieht, sieht man nichts *g*
Ich versteh das nicht mit dem Programmspeicher und dem Z-Register und dann muss ich das Z-Register um 1 erhöhen und wieder auslesen und und und...
Vor allem, wozu brauch ich unten zwei Labels mit daten?

Florian
13.08.2005, 12:30
http://www.mikrocontroller.net/tutorial/memorySchummler! *lol*
Da steht mein beispiel ja fast Wort für Wort! *rofl* *@ Thomas -> schau da mal*

@ Thomas:
Stell Dir ein Buchregal vor!
Mit dem Z-Register gibst Du nun die Position des Buches an.

ldi ZL , LOW (datenlabel * 2)
ldi ZH , HIGH (datenlabel *2)
Danach ziehst Du das passende Buch mit dem Befehl lpm aus dem Regal und packst es in den Einkaufswagen r0.
Da Du es aber nach dem bezahlen mit nach Hause nehmen möchtest musst Du es in einen Beutel packen, r16, da Du den Einkaufswagen nicht mitnehmen darfst/kannst bzw. willst -> für dieses beispiel!

Jetzt verstanden?

Wenn Du dann aber mehrere Bücher haben möchtest, musst Du mehrere Male einkaufen gehen.
Wenn Du vor dem regal stehst musst Du den Pointer für das Regal um eine Position (Byte) erhöhen, um das nächste Buch einzupacken!

Also mal in Stichworten:
- Datenpointer
- Schleife öffnen
- lpm
- r0 (lpm_reg) in r16 (temp) übertragen
- Datenzähler um 1 erhöhen
- Schleife wiederhohlen

Das wars!
Das ist eigentlich, wenn man es einmal verstanden hat, garnicht schwer!?



Ps:
Habs glatt überlesen! ;o)
Die beiden labels sind die Positionen für die daten, die Du auslesen willst, Dein Bücherregal!

Tekeli
13.08.2005, 12:33
Florian, kannst Du uns bitte das mit dieser 2 mal erklären.
Im Tutorial steht, dass
das unterste Bit gibt jeweils an, ob das untere oder obere Byte des Wortes im Programmspeicher geladen werden soll.
Sagen wir mal unser speicher beginnt bei b0000 0000 0000 0000 an.
Also

b0000 0000 0000 0000: 0011 1100 0101 0011

Wenn wir die Adresse mal 2 nehmen, bleibt diese ja gleich: 2x0 = 0
Also das unterste Bit der Adresse ist gleich 0. Dann landet 0011 1100 in das r0. Ist das richtig so?
Jetzt erhöhen wir die Adresse um eins:

b0000 0000 0000 0001: 0000 1111 1111 0000

Adresse mal 2 = b0000 0000 0000 0010 -> das unterste Bit ist wieder 0!

hmm, irgendwie versteh ich das nicht :(

toeoe
13.08.2005, 12:39
Soweit hab ich das erstmal verstanden mit dem Bücherregal, danke.
Hier mal mein Code:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister

.org 0x000
rjmp reset ;Interruptvektor "reset:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

ldi ZH, HIGH(daten1 * 2)
ldi ZL, LOW(daten1 * 2)

sei ;Interrupts zulassen

loop:
lpm ;nächstes Byte des String nach r0 einlesen
mov tmp, r0 ;Inhalt von R0 nach "tmp" kopieren
adiw ZL,1 ;Adresse des Z-Pointers um 1 erhöhen

rjmp loop ;immer wieder zu "loop:" springen

daten1:
.db "Hallo!" ;immer eine gerade Anzahl an Bytes
Bloß wie überprüfe ich, ob er auch wirklich den String "Hallo!" in tmp kopiert? Im Debug-Modus seh ich in r16 auch keine Strings..hmm..
@Tekeli: Danke für den Link, bin grad nicht drauf gekommen, da nachzuschauen *g* Habs dadurch auch erstmal besser verstanden, aber das mit der 2x hab ich immer noch nicht verstanden, also keine Sorge. Im Tut steht ja auch "Über die Multiplikation der Adresse mit 2 sollte man sich erst mal keine Gedanken machen: "das ist einfach so"™" Da hat der Florian wohl den Satz geklaut ;) oder er weiß es selber nicht *duck* *gg*

@Tekeli: Hast du dir den Wert "0011 1100 0101 0011" jetzt nur ausgedacht, oder muss ich verstehen, was dahinter steckt?

Florian
13.08.2005, 12:43
Hallo Tekeli!!
Das Zitat ist etwas kompliziert, das habe ich auch zuerst nicht verstanden!
Der Speicher ist so aufgebaut:
00 00 00 00 00 00
das sind jeweils zwei Bytes zusammengefasst in ein Word!
Das erste Word hat die Adresse 0x0000.
Wenn ich jetzt das Z lade, dann steht darin 2*0 = 0, das ist richtig!
Jetzt erhöhe ich das ganze um eins, ohne es mal 2 zu nehmen, das ist wichtig, das muss man nur bei der initalisierung des Z machen!
2*0 = 0 + 0 = 0 -> erste Adresse
2*0 = 0 + 1 = 1 -> nächste Adresse
2*1 = 2 + 0 = 2 -> nächste adresse
2*1 = 2 + 1 = 3 -> nächste Adresse

toeoe
13.08.2005, 12:45
Hmm..Achtung, doofe Frage, aber wieso addierst du einmal mit 0 und einmal mit 1? Also mit 1 addieren versteh ich ja, weil wir das nächste Byte haben wollen, aber mit 0?

Tekeli
13.08.2005, 12:46
Ok, alles klar!
Danke sehr!

Florian
13.08.2005, 12:47
Hey Thomas!
Herzlichen Glückwunsch!
Dein Code ist soweit richtig!
Kleiner Tipp: belege das R0 auch mit einem Namen über .def, z.B. lpm_reg!


*edit*:
War vielleicht etwas dumm, was ich da gemacht habe! *lol*
2*0 = 0 + 0 = 0 -> erste Adresse
erste Adresse + 1 = 1
zweite Adresse + = 2
.....................

toeoe
13.08.2005, 12:49
Huch, lügst du mich an? *g* Hehe, war ja wirklich nicht so schwer. Hab nun oben noch
.def lpm_reg = r0
geschrieben und im Code dementsprechend geändert.

Florian: Kannst du mir bitte noch sagen, wie ich das mit dem Simulato vom AVR Studio kontrollieren kann, ob er auch wirklich den String in r0 lädt?

Tekeli
13.08.2005, 12:52
Noch eine Frage.

Sollen wir die Zeichenkette mit '\0' selbst abschließen, damit wir wissen wann diese zu Ende ist, oder macht das Assembler für uns?

Florian
13.08.2005, 12:55
Sobald Du den Simulator gestartet hast (blauer Pfeil), klickst Du oben im Menü auf View und Memory!
Dann vergleichst Du, ob er die richtigen Bytes aus dem Speicher zieht!

Frage:
Was ist an dem Programm noch nicht perfekt?#


*edit*

Sollen wir Zeichenkette mit '\0' selbst abschließen, damit wir wissen wann diese zu Ende ist, oder macht das Assembler für uns?Das war meine Frage! *heul* *lol*
Nein, wir machen das nicht mit 0!
Rechnet mal mitte aus, wie viele Bytes es sind, die ihr da auslest!

toeoe
13.08.2005, 13:00
Hmm...seh da nicht richtig durch, Hab Memory, Memory2 und Memory3. Weiß auch nicht, auf welche Bytes ich da achten muss.




Frage:
Was ist an dem Programm noch nicht perfekt?

Das er immer wieder das Wort in r16 kopiert? Er sollte aufhören, wenn er das Wort fertig kopiert hat, oder?

6 Bytes? Da wir 6 Buchstaben haben? So hab ich das zumindest hierraus verstanden:

.db "Test", 0
ist äquivalent zu
.db 84, 101, 115, 116, 0

[edit]
Platz 7 in der Statistik ;)

Florian
13.08.2005, 13:03
Hmm...seh da nicht richtig durch, Hab Memory, Memory2 und Memory3. Weiß auch nicht, auf welche Bytes ich da achten muss.Nur den normalen Memory!
Rechts steht dann alles als ASCII-String, da musst Du dann nach Deinem Hallo! suchen und dann die Maus draufhalten, bis die Adresse erscheint und dann das passende Word dazu raussuchen!


Das er immer wieder das Wort in r16 kopiert? Er sollte aufhören, wenn er das Wort fertig kopiert hat, oder?Richtig, er geht auch über den Datenspeicher von uns hinaus! (siehe edit oben)

izaseba
13.08.2005, 13:09
Hallo Leute,
Ich habe es geschafft eben nachzuschauen, wie es Euch geht.
Florian hat Euch echt mit dem Holzhammer vor den Kopf gehauen \:D/
Das find ich gut, genau was Stimmt an diesem Code nocht nicht?
Der Florian hat nur Hallo gewollt, also irgendwie merken wann Hallo zu ende :-&
Ups, naja

Es ist jetzt wirklich wichtig, das zu verstehen, weil man den Flash nicht nur für das Programm an sich benutzen kann, sondern auch für Daten, so wie der Florian schon vor hat um z.B. Verschiedene Töne auszugeben (hab selber Angst vor, kenne mich mit Musik nicht aus)

Nur merk euch Ihr braucht erstmal nur den Z-Zeiger und lpm, der rest ist für SRam .

Ich glaube, ich mach mir auch ein Piezo dran, und mache hier mit.
Bis jetzt habe ich noch keine Musik mit dem AVR gespielt

toeoe
13.08.2005, 13:09
Ahh, ok, da seh ich das "Hallo!" Allerdings steht dadrunter auch noch was, was er ja auch noch in r16 dann kopiert, hab mal ein Bild angefügt.
Das kommt doch bestimmt durch das nichtvorhanden Nullbyte zustande, oder?

Florian
13.08.2005, 13:09
Genau, 6 Bytes!

Jetzt schaut euch mal bitte die beiden Vergleichsbefehle cp und cpc an!

Florian
13.08.2005, 13:13
Schön, dass Du da bist, Sebastian, in erklären bin ich echt ne NULL! *lol*
Holzhammer war nicht gewollt, aber scheinbar da! *unglücklich guck*

izaseba
13.08.2005, 13:18
Holzhammer war nicht gewollt
Der muß aber manchmal sein, kenn ich von mir!
Gut gemacht bis jetzt,
Ich muß leider los (bin gerade im Büro, und sehe da PC an O:) )
melde mich später wieder, bin gespannt, wie weit Ihr kommt.

Gruß Sebastian

Florian
13.08.2005, 13:19
Bis nachher Sebastian! *rette mich, ich kann das nicht erklären -> lol*

Tekeli
13.08.2005, 13:20
Also bräuchten wir noch ein Register, wo wir die Zahl der eingelesenen Zeichen aufbewahren. Und vergleichen ob diese Zahl = 6 ist.
(In unserem Fall). Wenn ja, Abbruch.

So in etwa?

toeoe
13.08.2005, 13:21
Oh, hi Sebastian und auch wieder tschö *g* Genieß die letzten Arbeitsstunden noch ;)

Florian: Doch du kannst das :P
Ich hab nicht viel über cp und cpc rausgefunden, nur das cp die unteren Bits vergleicht und cpc die oberen.
Aber so richtig verstanden hab ichs nicht. Wo find ich denn die oberen und unteren Bits?

Florian
13.08.2005, 13:23
Hallo Tekeli!
So in etwa stimmt das, aber Du musst das, was Du zu Anfang in ZL und ZH lädst nur nochmal in die Register laden und plus die 6 Bytes rechnen!
Das ist dann die Endbeschränkung!

Ich möchte die Codes sehen! *lol*

*ich geh mal kurz was essen, bin gleich wieder da*

toeoe
13.08.2005, 13:33
Hmm...ich hab das jetzt so gelöst:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def lpm_reg = r0 ;Mein lpm-Register
.def zaehler = r17 ;Mein Zählregister, prüft, ob das Wort zu Ende ist

.org 0x000
rjmp reset ;Interruptvektor "reset:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

ldi ZH, HIGH(daten1 * 2)
ldi ZL, LOW(daten1 * 2)

ldi zaehler, 0b00000000 ;Zähler auf 0 setzen

sei ;Interrupts zulassen

loop:
cpi zaehler, 0b00000110 ;Vergleich Zähler mit 6
breq loop ;wenn Zähler != 6, dann wieder zu "loop:" springen

einlesen:

lpm ;nächstes Byte des String nach r0 einlesen
mov tmp, lpm_reg ;Inhalt von R0 nach "tmp" kopieren
adiw ZL,1 ;Adresse des Z-Pointers um 1 erhöhen
inc zaehler ;Zähler um 1 erhöhen

rjmp loop ;wieder zurück zu "loop:"


daten1:
.db "Hallo!" ;immer eine gerade Anzahl an Bytes
Sorry, versteh die Befehle cp und cpc aber leider nicht (wegen untere Bytes und obere?) Aber mein Code geht auch.
Nachteil ist eben, dass von Anfang an klar sein muss, aus wieviel Zeichen dsa Wort besteht.

[edit]
Ich bin auch mal eben was essen.

izaseba
13.08.2005, 13:44
Nochmal eine Anmerkung (wie gut, daß mein Chef jetzt feiern ist)

Ein String wie "hallo!" von Florian ist nichts anderes als 6 Bytes die hintereinander im Speicher stehen.
Der Assembler erkennt aber, daß es sich um eine Zeichenkette handelt und wandelt selber
jeden Buchstaben in ASCII Zahlen um. ASCII ist steinalt, hat aber bis heute überlebt,
weil es recht simpel ist. Solltet Ihr Euch damit noch nicht auseinandergesetzt haben
hier (http://www.torsten-horn.de/techdocs/ascii.htm) ist eine Tabelle, wo man schön entnehmen kann welcher Buchstabe, welchem Wert entspricht.

Der Assembler wandelt auch selber um wenn man das so schreibt:
db. 'H','a','l','l','o','!'

Das ist Praktisch, sonst müßten wir uns selber darum kümmern.
Das werden wir auch nicht bei unsere Tonleiter brauchen,
hier werden wir sicher irgendwelche Werte im Speicher ablegen, um unsere Frequenzen
abzuspeichern.
Der Florian hat es nur mit dem Hallo genommen, um in die Thematik einzusteigen, denke ich

Sinnvoll sind nur ASCII Zeichen, wenn man z.B. über ein Terminal mit dem µC komuniziert, oder irgendwas auf dem LCD ausgeben möchte.
Das ist aber im Moment nicht das Ziel unserer Übung.
So jetzt verschwinde ich hier lieber :-$

Gruß Sebastian

Florian
13.08.2005, 13:52
Hallo Sebastian!
Danke nochmal für Deine Nacherklärung! ;o)

@ Thomas:
Was verstehst Du derzeit nicht?

@ Tekeli:
Was verstehst Du derzeit nicht?


Ps:
Bisher wollte ich nur zeigen, wie man Daten, die man im AVR-Flash speichert (.DB bzw. .DW), ausliest und in ein anderes Register schiebt, mehr nicht! ;o)
Das ganze hat bisher noch nichts mit Tönen zu tun!

izaseba
13.08.2005, 13:58
cp und cpc sind die wichtigsten Befehle im Assembler, und glaub mir Thomas, du hast
sie (zumindest cp) schon unbewußt benutzt!
cp hat aber nur Sinn in Verbindung mit SREG.
oder was meinst Du was die ganzen brne, breq usw. machen?
es sind keine Befehle für sich wenn Du Dir anschaust wieviele Takte sie brauchen, steht dort meistens 1/2 .
und warum? weil dort immer cp ausgeführt wird und jenachdem welcher Flag im SREG danach gesetzt (oder nicht gesetzt ist) wir gesprungen, oder halt nicht.
schreibe Dir ein Miniprogramm, wo Du zwei Register mit Werten fütterst.
Behandle sie dann mit hilfe von cp uns schaue, was sich im SREG so tut.
Schaue auch was die Bits von SREG zu bedeuten haben, und ändere die Zwei Werte im Register.
Dann wirst Du schon um einiges schlauer!

Gruß Sebastian

Florian
13.08.2005, 14:09
Na ihr Beiden, wie siehts aus?
Komt ihr voran?
Soll ich noch was erklären?
Ihr kennt doch auch cpi, oder?