PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Testroutine für Zählerüberlauf



cipoint
11.01.2007, 19:47
Hallo,

die Sache sieht so aus: Ein Zähler wird in einer Schleife um den Wert einer Variablen bei jedem Durchlauf verringert. Sobald der Zähler überläuft, muss ich das wissen. Gibts da ein Befehl? ATMega8

Bin ein Anfänger.

SprinterSB
11.01.2007, 20:00
Bei einem Underflow (Unterlauf) wird das Carry gesetzt. Also sollte BCS gehen.

cipoint
11.01.2007, 20:08
Den Befehl finde ich in der Dokumentation nicht. Wie benutze ich ihn?

SprinterSB
11.01.2007, 22:45
branch on carry set

Hanni
12.01.2007, 13:12
Der Befehl sollte BRCS heissen.

Eine andere durchaus übliche Art und Weise ist übrigens rückwärts bis Null zu zählen. Der Befehl um das zu prüfen ist ein simples BREQ direkt nach der Subtraktion.

Grüße,
Hanni

cipoint
12.01.2007, 14:13
Eine andere durchaus übliche Art und Weise ist übrigens rückwärts bis Null zu zählen. Der Befehl um das zu prüfen ist ein simples BREQ direkt nach der Subtraktion.i

Aber wenn das Ergebnis nach der Subtraktion nicht 0 sondern 255 ist? Ich ziehe ja nicht immer 1 ab ...

Ich will die Sache jetzt mit einem Timer lösen. Die Timer in ATMega8 bieten dafür sogar schon Lösungen. Den Tipp habe ich in einem anderen Forum bekommen. Aber ich weiß leider nicht, wie man die Timer konfiguriert. Hier auf roboternetz.de gibts ein Artikel über Timer, doch leider steht nichts drin, wie man das in Assembler löst.

izaseba
12.01.2007, 16:07
Aber ich weiß leider nicht, wie man die Timer konfiguriert


Schau mal hier (http://www.izaseba.roboterbastler.de/index.php?popup=Tutorial&section=Lektion10) nach, ich hab da einen Artikel über Timer geschrieben.

Gruß Sebastian

Hanni
12.01.2007, 16:09
gibts ein Artikel über Timer, doch leider steht nichts drin, wie man das in Assembler löst.

1. im Datenblatt das Kapitel über die Timer raussuchen.
2. die Bits für den gewünschen Modi notieren
3. die entsprechenden Register setzen.
4. fertig ....

cipoint
12.01.2007, 21:11
Aber ich weiß leider nicht, wie man die Timer konfiguriert


Schau mal hier (http://www.izaseba.roboterbastler.de/index.php?popup=Tutorial&section=Lektion10) nach, ich hab da einen Artikel über Timer geschrieben.

Gruß Sebastian

Kaum zu glauben, aber ich hatte schon vor, dir eine Mail zu schreiben, dass du eine Tut über Timer machst. =D>

izaseba
13.01.2007, 00:13
Kaum zu glauben, aber ich hatte schon vor, dir eine Mail zu schreiben, dass du eine Tut über Timer machst.


Tja, geplannt waren Timer sowieso, ich wäre auch gerne weiter damit, aber man hat nicht immer Zeit und Lust weiterzuschreiben....
Ich hoffe, daß Du damit was anfangen kannst, sonst lohnt es sich hin und wieder mal vorbeizuschauen, ob was neues dazugekommen ist :-)

Gruß Sebastian

cipoint
13.01.2007, 21:09
Irgendwie finde ich im Datenblatt vom ATMega8 kein Interrupr füt Match on Compare vom Timer0. Sagt jetzt bloß nicht, das Timer0 das nicht unterstützt.

izaseba
13.01.2007, 22:10
Sagt jetzt bloß nicht, das Timer0 das nicht unterstützt.


Tja was soll ich sagen...
Nimm den Timer 2....

Gruß Sebastian

cipoint
13.01.2007, 22:28
Also das mit dem Match on Compare lasse ich erstmal sein. Ich komme nämlich bei einem kleine Programm nicht weiter. Die LED leuchtet nicht. ;-)

Wo ist da der Fehler?

.include "m8def.inc"

.def temp = r16

.org 0x000
rjmp main
.org 0x009
rjmp timer

main:

ldi temp, LOW(RAMEND)
out SPL, temp
ldi temp, HIGH(RAMEND)
out SPH, temp

sbi DDRD, PD5

sei

loop:
rjmp loop

timer:
sbi PORTD, PD5
reti

Ich wette, dass da irgend ein Flag nicht gesetzt wurde.

izaseba
13.01.2007, 22:44
.org 0x009


Schreib besser


.org OVF0addr

Ist doch besser zu lesen, oder ?


Ich wette, dass da irgend ein Flag nicht gesetzt wurde.


Damit hättest Du die Wette gewonnen, schau mal im Dattenblatt nach :
TCCR0 ->Prescaller
TIMSK -> Interrupt beim Überlauf freischalten
eventuell
TCNT ->um den Timerwert vorzugeben...



ldi temp, LOW(RAMEND)
out SPL, temp
ldi temp, HIGH(RAMEND)
out SPH, temp


Mach es andersrum, zuerst High und dann LOW schreiben, es gibt da Registerpaare, wo das nicht egal ist!!


Gruß Sebastian

cipoint
14.01.2007, 11:02
Ich wollte mit SBI TCCR0, CS02 ein Bit in den Register setzen, aber der Compiler meldet: "Out of range".
Mit LDI temp, 0b00000100 und dann OUT TCCR0, temp hat es dann geklappt. Aber jetzt frage ich mich, wie ich einzelne Bits in dem Register manipulieren kann, ohne andere zu beeinflussen?

Noch was:
Ne ganz komische Sache. Beim ersten Überlauf wird der Interrupt aktiviert, alles ok. Der TOV0 Flag wird auch (automatisch) zurückgesetzt und die LED geht an. Beim zweiten Überlauf kommt das Programm aber nicht mehr aus der loop-Schleife und der TOV1 Flag wird zwar gesetzt, aber nicht mehr gelöscht. Auch die Befehle ldi temp, (1<<TOV0) und out TIFR, temp haben da nicht gebracht. Woran liegt das?

Hier der Quellcode:

.include "m8def.inc"

.def temp = r16

.org 0x000
rjmp main ; Reset Handler

.org OVF0addr
rjmp timer ; Timer0 Handler


main:

ldi temp, HIGH(RAMEND)
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp

sbi DDRD, PD5
cbi DDRD, PD2

ldi temp, 0b00000001
out TCCR0, temp

ldi temp, 0b00000001
out TIMSK, temp

sei

loop:
rjmp loop

timer:
sbis PORTD, PD5
rjmp setLED

sbic PORTD, PD5
rjmp clrLED

setLED:
sbi PORTD, PD5

ldi temp, (1<<TOV0)
out TIFR, temp

rjmp loop

clrLED:
cbi PORTD, PD5

ldi temp, (1<<TOV0)
out TIFR, temp

rjmp loop

Hanni
14.01.2007, 12:52
Ich wollte mit SBI TCCR0, CS02 ein Bit in den Register setzen, aber der Compiler meldet: "Out of range".

Wenn du nur das eine Bit ändern möchtest kannst du es auch so versuchen:


in r16, TCCR0
ori r16, (1<<CS02)
out TCCR0, r16



Noch was:
Ne ganz komische Sache. Beim ersten Überlauf wird der Interrupt aktiviert, alles ok. Der TOV0 Flag wird auch (automatisch) zurückgesetzt und die LED geht an. Beim zweiten Überlauf kommt das Programm aber nicht mehr aus der loop-Schleife und der TOV1 Flag wird zwar gesetzt, aber nicht mehr gelöscht. Auch die Befehle ldi temp, (1<<TOV0) und out TIFR, temp haben da nicht gebracht. Woran liegt das?

Das liegt dadran, das mein eine ISR (und timer ist eine) nicht mit rjmp #label sondern mit reti verlassen wird.

Grüße,
Hanni.

cipoint
14.01.2007, 13:20
Danke, dann schreib' ich das Programm um.

edit: Habe Schwierigkeiten, das zu realisieren. Bin eben noch sehr unerfahren, was den Stack angeht.
Also ein Flowchart habe ich schonmal angefertigt. Mit dem Quellcode klappt das alleridngs noch nicht.

http://www.cipoint.homepage.t-online.de/roboternetz/blink.jpg

Der uC befindet sich zunächst in einer Endlosschleife. Im Interrupthandler vom Timer0 (Label timer) wird dann mit sbic abgefragt, ob die LED an ist. Wenn nicht, springt der uC zum Label LEDon. Dort wird die LED angemacht. Danach kehrt der uC ja aber wieder zurück zum Label timer. Dort steht leider der Befehl sbis, nach welchem der Sprung zu LEDoff steht.

Wie verhindere ich also, dass der uC nicht nach LEDoff springt, wenn er im selben Durchlauf schon in LEDon war?

edit²: Habe das jetzt so gelöst.

include "m8def.inc"

.def temp = r16
.def oldStatus = r17

.org 0x000
rjmp main ; Reset Handler

.org OVF0addr
rjmp timer ; Timer0 Handler


main:

ldi temp, HIGH(RAMEND)
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp

sbi DDRD, PD5
cbi DDRD, PD2

ldi temp, 0b00000001
out TCCR0, temp

ldi temp, 0b00000001
out TIMSK, temp

sei

cbi PORTD, PD5

loop:
rjmp loop

timer:
sbic PORTD, PD5
rjmp LEDoff
sbi PORTD, PD5
reti

LEDoff:
cbi PORTD, PD5
reti
Ist das programmiertechnisch und stilistisch richtig?

Hanni
14.01.2007, 16:18
Ist das programmiertechnisch und stilistisch richtig?

Programmiertechnisch schon, über den Stil und die Lesbarkeit kann man sicher streiten.

Anstatt:


ldi temp, 0b00000001
out TCCR0, temp

ldi temp, 0b00000001
out TIMSK, temp


würde ich in dem Teil das folgende schreiben:


ldi temp, (1<<CS00)
out TCCR0, temp

ldi temp, (1<<TOIE0)
out TIMSK, temp


Das ganze ist meiner Ansicht nach vor allem besser lesbar.

Im übrigen sollte man in Interupt Routinen prinzipiell das Status Register SREG sichern. dieses kann z.B. wie folgt erfolgen:


timer:
push temp
in temp, SREG
push temp
sbic PORTD, PD5
rjmp LEDoff
sbi PORTD, PD5
rjmp timer_exit

LEDoff:
cbi PORTD, PD5

timer_exit:
pop temp
out SREG, temp
pop temp
reti


Dieses ist insbesondere dann wichtig, wenn in deinem Loop und in der ISR Operationen durchgeführt werden, bei denen Flags im SREG verändert werden können.

In deinem Programmablaufplan würde ich die Timer Interupt Routine komplett entkoppelt zeichnen. Zum einen, weil der µC nach dem Interupt exakt dort weitermacht, wo er aufgehört hat und zum anderen, weil es später weniger verwirrend wirkt.

Grüße,
Hanni

cipoint
14.01.2007, 17:24
Ist das programmiertechnisch und stilistisch richtig?
Anstatt:


ldi temp, 0b00000001
out TCCR0, temp

ldi temp, 0b00000001
out TIMSK, temp


würde ich in dem Teil das folgende schreiben:


ldi temp, (1<<CS00)
out TCCR0, temp

ldi temp, (1<<TOIE0)
out TIMSK, temp


Das ganze ist meiner Ansicht nach vor allem besser lesbar.

Aber wenn ich CS00, CS01 und CS02 manipulieren will, und zwar getrennt voneinander? Bleibt mir da nur die Möglichkeit, den kompletten Register auszulesen, mit OR oder AND zu bearbeiten und zurückzuschreiben?
Für I/O Register gibt es ja sbi und sbi. Und für "normale" Register?

Hanni
14.01.2007, 17:35
Aber wenn ich CS00, CS01 und CS02 manipulieren will

z.B.: ldi temp, (1<<CS00) | (1<<CS01)

zum setzen von 2 Bits.

cipoint
14.01.2007, 18:53
Ah, wieder was gelernt.

cipoint
18.01.2007, 18:53
@izaseba: Zu deinem TutorialII: Ich habe zwar rausbekommen, dass das Programm immer prüft, ob die Register für LedX gleich 0 sind und dementsprechend die Bits in tmp1 für die Ausgänge gesetzt werden. Aber was es mit den Operationen in #setze und #setze1 auf sich hat, bleibt mir ein Rätsel.