www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Atmega8 Assembler Timerproblem


Autor: Matze (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich versuche im Moment einen Timer zu realisieren. Bin noch ziemlich neu 
auf dem Gebiet und hab moch noch nicht an den internen Timer des 
Controllers herangetraut. Wollte einfach mal mit Taktschritten die 
abgezählt werden einen Timer zusammenbasteln, der von 0-9999ms zählt.

Ich habe den Controller schon so weit, dass ich in einem Speicher (r21) 
die 1er Schritte bis 99 und in einem anderen (r23) die 100er Schritte 
bis 9900 habe.

Nun habe ich versucht über die Taktzeiten einen Timer aufzubauen, der 
entsprechend den Speicherwerten 0-9999ms Herunterzählt bis die Blitz-LED 
leuchtet. Den Abschnitt habe ich mal in den Anhang gepackt.

Ansich macht er schonmal das richtige. Ich habe mal einen Zähler 
angeschlossen und folgende Probleme festgestellt: Zum einen nimmt die 
Zeit immer mehr ab, je höher sie wird. Also bei 10ms kommen noch 10ms 
raus, aber bei 50 sinds nur noch 49, bei 150 dann 145 und bei 300 
nurnoch 260. Ich glaube ich hab da irgendeine verschachtelung noch 
vergessen.

Zum anderen und das ist das größte problem: wenn ich den vorgang 
mehrmals wiederhole zählt er ab und an einfach mal genau 100ms drauf. 
also statt 50 dann 150ms. Ich kann mir das überhaupt nicht erklären da 
ich auch kein Muster erkennen kann. Was kann ich hier übersehen haben?


Vielen Dank für die Mühe schonmal

Mit freundlichen Grüßen

Matze

Autor: 3348 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zum einen enthaelt das AVRStudio einen Simulator, zum Anderen weshalb 
zaehlst du nicht mit einer 16bit variablen ?

Autor: Matze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hmm ich dachte ich habe bei einem 8bit controller auch nur 8bit 
speicherplätze. finde auch im datenblatt keine 16bit plätze.

Autor: Jochen Müller (taschenbuch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matze,

Du MUST die Datenblätter einfach SORGFÄLTIGER lesen, sonst macht die 
Beschäftigung mit einem MikroController überhaupt keinen Sinn!. Keinen.

Die Registerpaare X,Y,Z (xh:xl, yh:yl, zh:zl) sind 16Bit Register die 
für Pointer und 16-Bit Operationen z.B. ADIW ZH:ZL,2 verwendet werden 
können.

Aber das steht MEHR ALS DEUTLICH im Datenblatt bzw. im Instruction-Set.

Jochen Müller

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jochen Müller wrote:
> Matze,
>
> Du MUST die Datenblätter einfach SORGFÄLTIGER lesen, sonst macht die
> Beschäftigung mit einem MikroController überhaupt keinen Sinn!. Keinen.
>
> Die Registerpaare X,Y,Z (xh:xl, yh:yl, zh:zl) sind 16Bit Register die
> für Pointer und 16-Bit Operationen z.B. ADIW ZH:ZL,2 verwendet werden
> können.

Es sind keine 16-Bit-Register. Zusammen mit r25:r24 sind das die acht 
höchstwertigsten Register, auf die sich die adiw- und sbiw-Befehle 
anwenden lassen. Die sechst obersten Register haben zusätzlich die 
Besonderheit, dass sie bei einigen Befehlen als Pointer zur indirekten 
Adressierung verwendet werden können.
16-Bit-Operationen kannst du aber auch mit beliebigen anderen Registern 
machen, nur brauchst du dann zwei einzelne Befehle:
;r17:r16
clr r2
ldi r18, 5

add r16, r18
adc r17, r2
Damit wird das Registerpaar r17:r16 um 5 erhöht.

> Aber das steht MEHR ALS DEUTLICH im Datenblatt bzw. im Instruction-Set.

Korrekt.

Autor: Matze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wie gesagt bin da erst noch am anfang und versteh es mag sein dass ich 
das im datenblatt gelesen hab aber das heißt nicht dass ich es auch 
verstanden habe.

kann ich dann einfach meine beiden register mit den 1er und 100er 
stellen auf dieses kombinierte 16bit register überspielen und dieses 
dann über dec immer einen runterzählen lassen?

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

Bewertung
0 lesenswert
nicht lesenswert
Matze wrote:
> wie gesagt bin da erst noch am anfang und versteh es mag sein dass ich
> das im datenblatt gelesen hab aber das heißt nicht dass ich es auch
> verstanden habe.
>
> kann ich dann einfach meine beiden register mit den 1er und 100er
> stellen auf dieses kombinierte 16bit register überspielen und dieses
> dann über dec immer einen runterzählen lassen?

Nein.
dec ist eine 8 Bit Operation.
Du musst die sub, bzw. sbc Instruktionen benutzen. Auch wenn
der AVR ein 8 Bit Porzessor ist, bedeutet das nicht, dass man
keine 16 Bit Operationen machen kann. Im Grunde hast du das Prinzip
ja schon durchschaut, du teilst deine Zahl auf auf 2 Register.
Nichts anderes hast du mit deinen jetzigen Einern/Zehnern und
Hunderter gemacht. Nur ist so eine Aufteilung zwar für Anzeigezwecke
wunderbar, aber zum Rechnen hast du einiges an Mehraufwand.
Daher mcht man die Aufteilung anders: Ein Register enthält
alle 256-er (in Analogie zu den Einern/Hunderter) und das
andere alle Vielfachen von 256. Zusammen ergeben die beiden
Register dann
eine 16 Bit Zahl.
Und natürlich gibt es dann auch spezielle Befehle, mit denen man
über mehrere Register verteilte Zahlen bearbeiten kann. Speziell
für Addition und Subtraktion. Eine besondere Rolle spielt dabei
das Carry Bit im Statusregister, über welches der Übertrag von
einem Register in das nächste abgehandelt wird. Auch das hast du
im Grunde schon gemacht, immer dann wenn dein Einer/Zehner bei
99 angelangt ist, musstest du spezielle Aktionen machen, damit ein
Übertrag in die Hunderter stattfindet.
Machst du die Aufteilung der 16 Bit Zahl in 2 Register aber nach
dem HighByte/LowByte Prinzip (also: die Trennung bei 256), dann
kann dir die Hardware dabei unter die Arme greifen und diesen
Übertrag in Form des Carry Bits einfach zur Verfügung stellen bzw.
mit eigenen Befehlen kann dieser Übertrag einfach in der nächst
höheren Stelle berücksichtigt werden.

Von einer 16 Bit Zahl (die in 2 Registern, zb r16 und r17) gespeichert
ist, kann dann zb so aussehen:
    subi  r16, 1     ; von r16 1 abziehen. Wenn dabei ein Unterlauf
                     ; entsteht, wird das Carry auf 1 gesetzt, ansonsten
                     ; auf 0
    sbci  r17, 0     ; von r17 0 und das Carry Bit abziehen. 0 abziehen
                     ; mag etwas seltsam anmuten. Aber der Sinn des
                     ; Befehls besteht eigentlich darin, dass genau dann
                     ; 1 abgezogen wird, wenn das Carry Bit von der
                     ; vorhergehenden Subtraktion auf 1 gesetzt wurde.
                     ; es also in der kleineren Stelle einen Unterlauf
                     ; gab.

Autor: Matze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank, das hat mich schonmal einen Schritt weiter gebracht. Jetzt 
scheiter ich aber schon daran das Gegenstück zu den Befehlen subi und 
sbci zu finden. was bedeutet das immediate in den befehlen. denn der add 
und adc befehl machen ja das gleiche, nur dass sie das immediate nicht 
mit drin haben und einen register hinzuzählen und keine konstante.

Ich würde jetzt vom Prizip her so aufbauen: Erst zähle ich das Register 
das die 1er enthält in das 16bit Aegisterpaar. Dann zähle ich so oft die 
Konstante 100 hinzu bis das 100er Register auf Null ist. D.h. im Prinzip 
reicht mir dafür dir operationen die dein Vorredner Philipp Burch 
gemacht hat. nur kann ich da statt der 5 auch einfach 260 eingeben? oder 
bekommt der dann ein Problem. Außerdem verstehe ich da die Rolle des 
Registers r2 nicht ganz. Was machen die Befehle genau?

Ich finde ind em instruction manual immer nur ein einzelnes Beispiel zu 
den Befehlen. Wirklich verstehen tue ich dadurch nicht was da passiert.


Vielen Dank mal wieder für eure Mühe.

Gruß

Matze

Autor: Matze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin wieder einen Schritt weiter. Habe noch ein wenig auf der Seite 
hier herumgesucht - Respekt kann einem wirklich weiterhelfen in einigen 
Punkten. Auf jeden Fall habe ich da zummindest die das Gegenstück 
gefunden bzw. gesehen dass es das soi nicht gibt. Jetzt noch eine 
Verständnisfrage.

Wenn ich die zweite zahl mit adc hochzähle, und quasi immer 0 hinzuzähle 
(war das auch der sinn des registers r2?) dann wird da immer einer 
hinzugezählt wenn in der operation vorher das register übergelaufen ist. 
Wird dann der rest entsprechend in dem ersten register gespeichert?

verstehe ich das richtig: meine zahl teilt sich dann so auf dass ich 
erst schaue wie gros das zweite register ist und den wert dann mit 256 
multiplizieren muss und dazu dann den inhalt des ersten registers zähle?

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matze wrote:
> Ich bin wieder einen Schritt weiter. Habe noch ein wenig auf der Seite
> hier herumgesucht - Respekt kann einem wirklich weiterhelfen in einigen
> Punkten. Auf jeden Fall habe ich da zummindest die das Gegenstück
> gefunden bzw. gesehen dass es das soi nicht gibt. Jetzt noch eine
> Verständnisfrage.

Ein Gegenstück brauchst du eigentlich nicht, da Addition und Subtraktion 
im Grunde die gleiche Operation ist. Anstatt "addi r16, 5" (Was es nicht 
gibt), kannst du einfach schreiben
subi r16, -5
, du subtrahierst also -5.

> Wenn ich die zweite zahl mit adc hochzähle, und quasi immer 0 hinzuzähle
> (war das auch der sinn des registers r2?) dann wird da immer einer

Ja.

> hinzugezählt wenn in der operation vorher das register übergelaufen ist.
> Wird dann der rest entsprechend in dem ersten register gespeichert?

Hm, was meinst du mit "Rest"? Beim ersten Aufruf von "add" wird ja nur 
das Low-Byte erhöht, was einen Überlauf verursachen kann. Im Falle eines 
Überlaufs ist das Carry-Bit anschliessend 1 und wird beim folgenden 
Aufruf von "adc" einfach zum Operanden addiert. Da der Summand r2 dort 
aber ohnehin 0 enthält, wird einfach noch 0 oder 1 (Je nach Carry-Bit) 
dazugerechnet. Der Aufruf von "adc" verändert das Low-Byte aber nicht 
mehr.

> verstehe ich das richtig: meine zahl teilt sich dann so auf dass ich
> erst schaue wie gros das zweite register ist und den wert dann mit 256
> multiplizieren muss und dazu dann den inhalt des ersten registers zähle?

Ohne Gewähr:
;Zahl zum Aufteilen befindet sich in r17:r16, die 10'000er liegen
;anschliessend in r18, die Tausender in r19 usw.
;r2 muss 0 enthalten.
;Verändert werden zusätzlich noch r23 und r24.
Split16:
  clr r18
  clr r19
  clr r20
  clr r21
  clr r22
  ldi r23, LOW(10000)
  ldi r24, HIGH(10000)
  Split16_loop1:
    inc r18
    sub r16, r23
    sbc r17, r24
  brcc Split16_loop1
  dec r18
  add r16, r23
  adc r17, r24
  ldi r23, LOW(1000)
  ldi r24, HIGH(1000)
  Split16_loop2:
    inc r19
    sub r16, r23
    sbc r17, r24
  brcc Split16_loop2
  dec r19
  add r16, r23
  adc r17, r24
  ldi r23, 100
  Split16_loop3:
    inc r20
    sub r16, r23
    sbc r17, r2   ;r2 = 0
  brcc Split16_loop3
  dec r20
  add r16, r23
  adc r17, r2
  ldi r23, 10
  Split16_loop4:
    inc r21
    sub r16, r23
  brcc Split16_loop4  ;Die Zahl ist nun auf jeden Fall kleiner als 100
  dec r21
  add r16, r23
  mov r22, r16        ;Die Einer befinden sich jetzt bereits in r16 (Rest)
ret
Ist ungetestet, sollte aber irgendwie in der Art funktionieren. r2 
liesse sich unter Verwendung von "sbci" wohl auch noch einsparen, ein 
globales Nullregister zu haben hat sich aber in meinen Augen bewährt.

Autor: Matze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe das jetzt mal versucht soweit. Kann das son funktionieren? 
Ausgangspunkt sind die Register r21 und r23 mit den 1ern und 100ern. Am 
ende soll die dadurch gespeicherte Zahl in ein 16bit Register r21 Low 
und r20 High gespeichert werden.

             ldi    r20, 0        ;r20 als High Register leer machen
             ldi    r29, 100      ;100 laden zum draufzählen
             ldi    r30, 0        ;0 ladem zum draufzählen
verteilung:  cpi    r23, 0        ;prüfen ob 100er vorhanden sind
             breq  ende_Tneu      ;abbrechen wenn keine 100er da sind (r21 ist Low Register und bereits gefüllt)
             add    r21, r29      ;zum Low Register r21 100 hinzuzählen
             adc    r20, r30      ;zum High-Register r20 0 plus dem Carrybite hinzuzählen
             dec    r23          ;einmal 100 sind nun aus r23 übertragen
             rjmp  verteilung      ;das Ganze von vorne


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

Bewertung
0 lesenswert
nicht lesenswert
Matze wrote:
> Habe das jetzt mal versucht soweit. Kann das son funktionieren?

Du musst lernen dir selbst zu helfen.
Im AVR Studio gibt es einen Simulator, der deinen Prozessor
simulieren kann.
Also: Klopf dein Codestück hinein, assembliere es und gehe es
im Simulator Schritt für Schritt durch. Dabei schaust du dir
nach jedem Schritt die CPU Register an und entscheidest ob
der tatsächliche Registerinhalt deinen Vorstellungen entspricht.

> Ausgangspunkt sind die Register r21 und r23 mit den 1ern und 100ern.

Wozu brauchst du die noch?
Ich hätte jetzt eher mal das Gegenteil erwartet:
Ein Codestück, welches eine Zahl in Hunderter, Zehner und Einer
zerlegt, weil du das für die Anzeige brauchst. Bzw überhaupt
eine Anzeigeroutine, die während des Aufbereitens der Anzeige
diese Zerlegung durchführt.

> Am
> ende soll die dadurch gespeicherte Zahl in ein 16bit Register r21 Low
> und r20 High gespeichert werden.
>
>
> 
>              ldi    r20, 0        ;r20 als High Register leer machen
>              ldi    r29, 100      ;100 laden zum draufzählen
>              ldi    r30, 0        ;0 ladem zum draufzählen
> verteilung:  cpi    r23, 0        ;prüfen ob 100er vorhanden sind
>              breq  ende_Tneu      ;abbrechen wenn keine 100er da sind
> (r21 ist Low Register und bereits gefüllt)
>              add    r21, r29      ;zum Low Register r21 100 hinzuzählen
>              adc    r20, r30      ;zum High-Register r20 0 plus dem
> Carrybite hinzuzählen
>              dec    r23          ;einmal 100 sind nun aus r23 übertragen
>              rjmp  verteilung      ;das Ganze von vorne
> 
> 

Sieht erst mal an sich gut aus. Aber noch mal der Hinweis: Eigentlich
brauchst du die umgekehrte Funktionalität.
Du musst schon konsequent sein. Dein bisheriges Schema mit Einer/Zehner
in einem Register und den Hundertern in einem anderen Register gibt
es nicht mehr.
Stattdessen hast du ein Registerpärchen in dem der Zähler als 16 Bit
Zahl realisiert ist. Bei Tastendruck wird der Zähler um 1 erhöht,
per Timer wird dieser Zähler um 1 erniedrigt.

Autor: 3349 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Matze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Antwort.

Bei mir siehts so aus. Das Programm ist mittlerweile riesig weil es halt 
mein "Lernprogramm" ist und da an jeder Ecke neue Dinge hinzugekommen 
sind.

Ich habe halt zunächst eine Zählroutine, wobei man drei tasten hat - 
eine zum hochzählen, eine runter und eine bestätigen. dann wird das 
ganze im display angezeigt. desshalb habe ich die zählroutine von 
vornherein auf zwei register verteilt wobei das eine bis 99 zählt und 
das zweite ab da an die 100er stellen zählt. damit lässt sich das im 
display schön auflösen.

nun möchte ich mit dem vorher eingestellten wert einen timer ablaufen 
lassen und genau treten halt die unstimmigkeiten auf die ich eingangs 
genannt habe. desshalb wollte ich das gerne mal in 16bit zählweise 
machen, sodass ich nicht manuell 100er und 1er bzw. 10er stellen 
runterzähle sondern das über die 16bit registerkombination löse.

dafür muss ich die 100er und 1er stellen halt auf zwei register 
aufteilen wie mein codebeispiel es hoffentlich macht.

habe bislang nicht das avr studio genutzt. werde mir das mal 
installieren.

aber ich denke ich hab die grundlagen für die 16bit register und das 
Carrybit gelernt - das war mir bislang noch etwas unklar.

Vielen Dank soweit!

Gruß

Matze

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich habe halt zunächst eine Zählroutine, wobei man drei tasten hat -
> eine zum hochzählen, eine runter und eine bestätigen. dann wird das
> ganze im display angezeigt. desshalb habe ich die zählroutine von
> vornherein auf zwei register verteilt wobei das eine bis 99 zählt und
> das zweite ab da an die 100er stellen zählt. damit lässt sich das im
> display schön auflösen.

So würde ich da nicht rangehen. Besser wär's, du würdest immer mit einer 
normalen 16-Bit-Zahl arbeiten und diese erst zur Anzeige in einzelne 
Ziffern (10'000er, 1'000er, 100er, 10er und 1er) zerlegen. Wenn du nicht 
den ganzen Zahlenbereich (0 - 65'535 bei 16 Bit) brauchst, kannst du 
natürlich die 1'000er und die 10'000er auch weglassen. Eine Möglichkeit 
für sowas habe ich ja oben bereits gepostet.

> nun möchte ich mit dem vorher eingestellten wert einen timer ablaufen
> lassen und genau treten halt die unstimmigkeiten auf die ich eingangs
> genannt habe. desshalb wollte ich das gerne mal in 16bit zählweise
> machen, sodass ich nicht manuell 100er und 1er bzw. 10er stellen
> runterzähle sondern das über die 16bit registerkombination löse.

Ja, das ist ja auch richtig. So arbeitest du immer mit einer normalen 
16-Bit-Zahl und extrahierst die Dezimalziffern erst bei der Anzeige.

> dafür muss ich die 100er und 1er stellen halt auf zwei register
> aufteilen wie mein codebeispiel es hoffentlich macht.

Dein Code erstellt aus drei Ziffern eine 16-Bit-Zahl, das ist eher der 
umgekehrte Weg, den man z.B. bei der Eingabe von Zahlen über eine 
Tastatur beschreiten sollte. Je nach verwendetem Controller wäre dann 
die Benutzung des Hardware-Multipliers angebracht.

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.