Forum: Mikrocontroller und Digitale Elektronik Probleme mir "langem Timer" (Kaskadierung)


von Martin Z. (zilluss)


Lesenswert?

Hallöchen, ich versuche mir grad ein Gerät zu basteln welches Klarträume 
induzieren soll d.h. nach ca. 4-5 Stunden für eine kurze weile LED's zum 
Blinken bringen soll. Dafür muss ich nun also lange Timer durch 
kaskadierung von Registern hinbekommen. Da ich aber schwer jedes mal so 
lange warten kann (bzw. auch mit kürzer Eingestellter Zeit finde ich den 
Fehler nicht) bleibt mir nichts anderes übrig als hier zu fragen. Das 
ganze läuft auf einem Attiny15 mit 1,6 MHz.
1
.include "tn15def.inc"
2
3
.org  0x0000     rjmp RESET
4
.org  INT0addr  nop
5
.org  PCI0addr  nop
6
.org  OC1addr    nop
7
.org  OVF1addr  nop
8
.org  OVF0addr  rjmp T0_OVF
9
.org  ERDYaddr  nop
10
.org  ACIaddr    nop
11
.org  ADCCaddr  nop
12
13
14
.def temp     = r16
15
.def subcount0   = r17
16
.def subcount1   = r18
17
.def subcount2   = r19
18
.def comp0     = r20
19
.def comp1     = r21
20
.def comp2     = r22
21
.def oncounter  = r23 
22
23
24
RESET:
25
26
27
clr subcount0
28
clr subcount1
29
clr subcount2
30
clr oncounter
31
32
;Die Vergleichswerte für die "Untertimer"
33
ldi comp0, 255
34
ldi comp1, 140
35
ldi comp2, 3
36
37
38
ldi temp, 0b00000010 ;TOIE0 auf 1
39
out TIMSK, temp
40
41
ldi temp, 0b00000101 ;Prescaler 1024
42
out TCCR0, temp
43
44
ldi temp, 0b00000011 ;PB0 & PB1 als Ausgang
45
out DDRB, temp
46
47
ldi temp, 0b00000011 ;STK500 SETUP
48
out PORTB, temp
49
50
sei
51
52
53
MAIN_LOOP:
54
55
cp subcount0, comp0
56
breq SUBINC1
57
58
cp subcount2, comp2
59
breq TIMEUP
60
61
rjmp MAIN_LOOP
62
63
64
SUBINC1:
65
inc subcount1
66
67
cp subcount1, comp1
68
breq SUBINC2
69
70
rjmp MAIN_LOOP
71
72
73
SUBINC2:
74
clr subcount1
75
inc subcount2
76
77
rjmp MAIN_LOOP
78
79
80
TIMEUP:
81
clr subcount0
82
clr subcount1
83
clr subcount2 
84
ldi comp0, 0
85
ldi comp1, 0
86
ldi comp2, 0
87
88
inc oncounter
89
cpi oncounter, 255
90
breq SHUTDOWN
91
92
sbis PORTB, 0
93
ldi temp, 0b00000011 
94
sbic PORTB, 0
95
ldi temp, 0b00000000 
96
out PORTB, temp
97
98
rjmp MAIN_LOOP
99
100
101
SHUTDOWN:
102
ldi temp, 0b00110000
103
out MCUCR, temp
104
sleep
105
106
107
T0_OVF:
108
inc subcount0
109
reti

von icke (Gast)


Lesenswert?

Alles gezähle in den timer int legen und dort ein flag setzen.

von Martin Z. (zilluss)


Lesenswert?

Hi, danke für die Antwort! Könntest du etwas genauer erklären wie du das 
mit der Flag meinst weil ich nicht verstehe wie ich damit auf so einen 
hohen wert komme.

von Karl H. (kbuchegg)


Lesenswert?

Martin Z. schrieb:
> Hi, danke für die Antwort! Könntest du etwas genauer erklären wie du das
> mit der Flag meinst weil ich nicht verstehe wie ich damit auf so einen
> hohen wert komme.


Gar nicht.
Die Flagge (einfach ein Register) zeigt nur an, dass die Zeit abgelaufen 
ist.

Am Anfang wird die Flagge (=such dir ein Register dafür aus) auf 0 
gesetzt.
Wenn 60 mal 1 Sekunde abgelaufen ist, ist 1 Minute vergangen
Sind 60 Minuten vergangen, dann ist 1 Stunde vergangen.
Sind bsp. 4 Stunden vergangen, dann setzt du ein spezielles Register (= 
die Flagge) auf 1

Das Hochzählen der Sekunden, Minuten, Stunden und entscheiden ob die 
Flagge auf 1 gesetzt werden muss, kann alles in der Interrupt Routine 
passieren.

In der Hauptschleife wartest du nur noch darauf, dass dieses Register 1 
wird. Da dies, so wie die Interrupt Routine aufgebaut ist, erst nach 4 
Stunden passieren wird, hat deine Hauptschleife effektiv 4 Stunden 
gewartet.

von Peter D. (peda)


Lesenswert?

Das sieht ja höllisch kompliziert aus, warum benutzt Du nicht die 
Möglichkeit, Bytes zu kaskadieren?
Der AVR kann ganz einfach 32 Bit (und mehr) Increment und Test auf 0:
1
dec_32_bit_timer:
2
  subi r16, byte1(1)
3
  sbci r17, byte2(1)
4
  sbci r18, byte3(1)
5
  sbci r19, byte4(1)
6
  breq Timer_abgelaufen
7
  ret
8
Timer_abgelaufen:
9
; mache was
10
  ret



Peter

von APW (Gast)


Lesenswert?

@Peter

Ich kenne mich zwar mit AVR-Asm nicht aus, aber
wenn in deinem Beispiel z.B. R16, R17, byte1 <> 0 sind,
R18,R19,byte2..byte4 aber =0, dann
setzt sbci r19, byte4(1) das Zero-flag und der anschliessende Zero-Test 
verzweigt fälschlicherweise nach Timer_abgelaufen.
Oder sehe ich das falsch ?

von Falk B. (falk)


Lesenswert?

Jeder Prozessor kann mit grossen Zahlen umgehen, wenn der Programmierer 
auch nur einen Hauch von Plan hat.

AVR-Tutorial: Arithmetik

MfG
Falk

von Peter D. (peda)


Lesenswert?

APW schrieb:
> Ich kenne mich zwar mit AVR-Asm nicht aus, aber
> wenn in deinem Beispiel z.B. R16, R17, byte1 <> 0 sind,
> R18,R19,byte2..byte4 aber =0, dann
> setzt sbci r19, byte4(1) das Zero-flag und der anschliessende Zero-Test
> verzweigt fälschlicherweise nach Timer_abgelaufen.
> Oder sehe ich das falsch ?

Ja.
Das Zero-Flag wird mit dem vorherigen verundet.
Nennt sich "zero-flag propagation".


Peter

von Martin Z. (zilluss)


Lesenswert?

Vielen Dank für die Antworten. In Asm bin ich leider nicht so bewandert 
und C für den Tiny15 "einzurichten" ist mir für so ein Mini-Projekt zu 
aufwendig. Ausserdem lern ich so ja was.

von Martin Z. (zilluss)


Lesenswert?

@Peter: ret geht mit dem Tiny15 leider nicht aber das ist nicht das 
Problem. Ich versteh nicht ganz was du in deinem Code machst. du zählst 
ja quasi runter wenn ich das richtig seh aber was bedeutet byteN(1) ?

von Stefan E. (sternst)


Lesenswert?

Martin Z. schrieb:
> ret geht mit dem Tiny15 leider nicht

Klar geht das.

> Ich versteh nicht ganz was du in deinem Code machst. du zählst
> ja quasi runter wenn ich das richtig seh aber was bedeutet byteN(1) ?

Das sind die jeweiligen Bytes, wenn man die 1 als 32-Bit-Wert (also 4 
Bytes) interpretiert. Er hätte auch beim subi direkt 1 und bei den sbci 
jeweils 0 schreiben können.

von Martin Z. (zilluss)


Lesenswert?

Achso ich dachte das Byte steht für irgendwas. also man zieht einfach 
nur 1 ab und gibt das carry weiter. Jetzt hab ichs verstanden, DANKE :-) 
!

wegen ret: Als ich es mit dem AVR Simulator auspropiert hab hatte ich 
einen Stack underflow. Es will glaube ich nur mit rcall funktionieren 
und nicht mit branches (weil bei branches nichts in den Z-Pointer 
schreiben glaub ich) aber wissen tu ich das nicht :-[ . Aber du merkst 
ja viel glauben und nichts wissen (klappt bei den Christen aber auch 
irgendwie)

von Stefan E. (sternst)


Lesenswert?

Martin Z. schrieb:

> wegen ret: Als ich es mit dem AVR Simulator auspropiert hab hatte ich
> einen Stack underflow. Es will glaube ich nur mit rcall funktionieren
> und nicht mit branches (weil bei branches nichts in den Z-Pointer
> schreiben glaub ich) aber wissen tu ich das nicht :-[

Natürlich geht es nicht mit Branches, die haben mit ret rein gar 
nichts zu tun. Branches sind "Sprünge" keine "Aufrufe".
(und mit dem Z-Pointer hat das Ganze auch nichts zu tun)

von Martin Z. (zilluss)


Lesenswert?

Also der Code funktioniert soweit recht gut. Allerdings hab ich das 
Problem das die Zeit extrem ungenau ist. Wenn ich z.B. bei 1600000Hz den 
Timer mit dem Prescaler 1024 betreibe hab ich eine Zählfrequenz von 
1600000/1024/256 = 6,1 Hz. Das heißt wenn ich als Zählvariable 265 nehme 
komme ich auf eine Zeit von ~41 Sekunden bis das Blinken losgeht. Ich 
kriege allerdings eine Zeit von 60 Sekunden wenn ich stoppe. Kann das 
interne Quarz wirklich SO ungenau sein oder könnte der Fehler woanders 
liegen?

Der gekürzte Code:
1
RESET:
2
ser oncounter
3
ldi countbyte0, 0b00000000
4
ldi countbyte1, 0b00000001
5
ldi countbyte2, 0b00000000
6
7
MAIN_LOOP:
8
nop
9
rjmp MAIN_LOOP
10
11
12
TIMEUP:
13
14
ldi countbyte0, 1
15
subi oncounter, 1
16
breq SHUTDOWN
17
18
sbis PORTB, 0
19
ldi temp, 0b00000011 
20
sbic PORTB, 0
21
ldi temp, 0b00000000 
22
out PORTB, temp
23
24
reti
25
26
T0_OVF:
27
subi countbyte0, 1
28
sbci countbyte1, 0
29
sbci countbyte2, 0
30
breq TIMEUP
31
32
reti

von Ahem (Gast)


Lesenswert?

Es gibt keinen "internen Quarz" nur einen "internen RC-Oszillator". Der 
ist leider ungenau.
Man kann ihn kalibrieren, aber für Laufzeiten von mehreren Stunden 
müsste man öfter kalibrieren. Also am besten einen Quarz verwenden bzw. 
Quarzoszillator. Wie genau muss es denn effektiv sein?

von Martin Z. (zilluss)


Lesenswert?

Ich kalibrier den Controller jetzt mit dem Wert den ich mit dem STK500 
ausgelesen hab. Ist zwar immernoch ein bisschen ungenau aber immerhin 
sind es keine 50% mehr :D

vorher hatte ich bei einer 40 Sekunden Zählungen in echt 60 Sekunden 
jetzt sind es 42(vielleicht ist das die Antwort?) oder 43. Wenn ich 
jetzt noch an dem Kalibrierungswert feile ist es ausreichend genug. 
Danke für die Hilfe!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.