www.mikrocontroller.net

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


Autor: Martin Z. (zilluss)
Datum:

Bewertung
0 lesenswert
nicht 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.
.include "tn15def.inc"

.org  0x0000     rjmp RESET
.org  INT0addr  nop
.org  PCI0addr  nop
.org  OC1addr    nop
.org  OVF1addr  nop
.org  OVF0addr  rjmp T0_OVF
.org  ERDYaddr  nop
.org  ACIaddr    nop
.org  ADCCaddr  nop


.def temp     = r16
.def subcount0   = r17
.def subcount1   = r18
.def subcount2   = r19
.def comp0     = r20
.def comp1     = r21
.def comp2     = r22
.def oncounter  = r23 


RESET:


clr subcount0
clr subcount1
clr subcount2
clr oncounter

;Die Vergleichswerte für die "Untertimer"
ldi comp0, 255
ldi comp1, 140
ldi comp2, 3


ldi temp, 0b00000010 ;TOIE0 auf 1
out TIMSK, temp

ldi temp, 0b00000101 ;Prescaler 1024
out TCCR0, temp

ldi temp, 0b00000011 ;PB0 & PB1 als Ausgang
out DDRB, temp

ldi temp, 0b00000011 ;STK500 SETUP
out PORTB, temp

sei


MAIN_LOOP:

cp subcount0, comp0
breq SUBINC1

cp subcount2, comp2
breq TIMEUP

rjmp MAIN_LOOP


SUBINC1:
inc subcount1

cp subcount1, comp1
breq SUBINC2

rjmp MAIN_LOOP


SUBINC2:
clr subcount1
inc subcount2

rjmp MAIN_LOOP


TIMEUP:
clr subcount0
clr subcount1
clr subcount2 
ldi comp0, 0
ldi comp1, 0
ldi comp2, 0

inc oncounter
cpi oncounter, 255
breq SHUTDOWN

sbis PORTB, 0
ldi temp, 0b00000011 
sbic PORTB, 0
ldi temp, 0b00000000 
out PORTB, temp

rjmp MAIN_LOOP


SHUTDOWN:
ldi temp, 0b00110000
out MCUCR, temp
sleep


T0_OVF:
inc subcount0
reti

Autor: icke (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alles gezähle in den timer int legen und dort ein flag setzen.

Autor: Martin Z. (zilluss)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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:
dec_32_bit_timer:
  subi r16, byte1(1)
  sbci r17, byte2(1)
  sbci r18, byte3(1)
  sbci r19, byte4(1)
  breq Timer_abgelaufen
  ret
Timer_abgelaufen:
; mache was
  ret



Peter

Autor: APW (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Falk Brunner (falk)
Datum:

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

AVR-Tutorial: Arithmetik

MfG
Falk

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Martin Z. (zilluss)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin Z. (zilluss)
Datum:

Bewertung
0 lesenswert
nicht 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) ?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin Z. (zilluss)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Martin Z. (zilluss)
Datum:

Bewertung
0 lesenswert
nicht 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:
RESET:
ser oncounter
ldi countbyte0, 0b00000000
ldi countbyte1, 0b00000001
ldi countbyte2, 0b00000000

MAIN_LOOP:
nop
rjmp MAIN_LOOP


TIMEUP:

ldi countbyte0, 1
subi oncounter, 1
breq SHUTDOWN

sbis PORTB, 0
ldi temp, 0b00000011 
sbic PORTB, 0
ldi temp, 0b00000000 
out PORTB, temp

reti

T0_OVF:
subi countbyte0, 1
sbci countbyte1, 0
sbci countbyte2, 0
breq TIMEUP

reti

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Martin Z. (zilluss)
Datum:

Bewertung
0 lesenswert
nicht 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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.