- fchao-Sinus-Wechselrichter AliExpress         
Ergebnis 1 bis 10 von 30

Thema: Zeitschleife Mikrocontroller

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    07.04.2015
    Beiträge
    903
    Hatte ich ja fast geahnt, dass Du die Hexadezimalschreibweise nicht kennst.

    Führende "$" oder "0x" vor Zahlen kennzeichnen im AVR Assembler die Hexadezimaldarstellung einer Zahl.

    Statt der Zehnerbasis unseres herkömmlichen Zahlensystems verwendet diese die 16er-Basis. Mit der 16er-Basis stellst Du einen Bytewert mit 2 Stellen dar.
    Die Praxis. Du zählst: 0,1,2,3,4,5,6,7,8,9, STOPP, jetzt kommt nicht 10, es geht weiter mit A,B,C,D,E,F, STOPP, jetzt kommt nicht G, weil mit "F" hast du eigentlich "15" gemeint, jetzt geht es weiter mit der nächsten Stelle, also 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 20,...
    bei FF hast Du damit den höchstmöglichen Wert 255 für ein Byte erreicht.

    Sinn dieses durchaus für Außenstehende völlig unverständlichen Machwerkes der Hexadezimalcodierung ist die Zusammenfassung eines Bytes (8 Bits) in zwei Zeichen. Du fasst dabei jeweils die unteren 4 und die oberen vier Bits zu einem Zeichen 0..F zusammen.

    Witz des ganzen: Geübte rechnen Dir in Windeseile diese Hexadezimalzahlen in reale Zahlen um und auch wieder zurück, aber nicht nur dass: Am Zahlenwert 201 musst Du erst rechnen, ob das vierte Bit von unten gesetzt ist oder nicht. In der Hexadezimalschreibweise ist das ein Hingucken: 4tes Bit ist in der unteren Gruppe, also in der 9. 9 ist größer als 8, also ist die 8 gesetzt, also ist das vierte Bit von unten gesetzt.

    Die Hexadezimalschreibweise ist also ein guter Kompromiss zwischen der normalen Ganzzahldarstellung (einzelne Bitwerte lassen sich nicht ablesen) und der Binärschreibweise (8 Stellen sind zu lang zum Schreiben)

    Kleine Übung: Wenn Du's nicht verstanden hast (ich bin kein Lehrer), Google es woanders nach, aber spätestens dann müsstest Du mir sagen können, was die Zahl $AA in unserem Zahlensystem ist. Wenn Du das nicht drauf hast, brauchst Du mit Assembler nicht weiter zu machen. Das begegnet Dir so oft, da kann man sich kaum verweigern.

    Die dritte Schleife brauchst Du nicht, weil du mit dem Zahlenbereich der äußeren Schleife hinkommst. Statt 20000 Takten willst Du 80000 Takte warten, daher kannst Du den Schleifenzähler $21 mit 4 multiplizieren, das macht dann $84.
    Geändert von Holomino (14.08.2015 um 21:15 Uhr)

  2. #2
    Neuer Benutzer Öfters hier
    Registriert seit
    14.08.2015
    Beiträge
    16
    oh, eigentlich weiß ich was die hexadezimaldarstellung ist und wie diese berechnet wird. $AA entspricht doch 170 (Dezimalzahl). Ich weiß dass bei 16 MHz und 5 ms 80000 Takte notwendig sind, dass was ich nicht weiß wie ich quasi die Durchläufe ( Hexazahlen) bestimmen soll....
    Zum Beispiel wenn ich eine dritte Schleife dazufüge..

    ldi R17, ?????
    WGLOOP0: ldi R18, ?????
    WGLOOP1: ldi R19, ?????
    WGLOOP2: dec R19
    brne WGLOOP2
    dec R18
    brne WGLOOP1
    dec R17
    brne WGLOOP0

    Danke

  3. #3
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    07.04.2015
    Beiträge
    903
    Die beiden inneren Loops durchlaufenen 20000 Takte. Du willst 80000 Takte haben? Dann musst Du wohl die inneren Loops vier mal durchlaufen. Dann nimm doch 4 als Startwert für die äußere Loop. Wo ist das Problem? Ob Du den Prozessor 100 mal bis zehn oder zehn mal bis hundert zählen lässt, ist dem ziemlich egal. Der merkt nicht, wenn er sich in der Nase bohrt.

    Warum Du allerdings darauf bestehst, eine dritte Loop außen rum zu bauen, verstehe ich noch nicht. Das kostet Dich ein Register für den dritten Schleifenzähler. Assembler ist keine Hochsprache, Register werden nicht wie Variablen gescoped. Wenn Du also dieses dritte Register zufällig oder beabsichtigt vor dem Aufruf Deiner Wartefunktion verwendest, ist der Inhalt nach dem Durchlauf der Wartefunktion wech.
    Allgemeine Maßnahmen dagegen sind:
    - Verwende Register pragmatisch in einer Routine (so wenige wie möglich. So viele, wie nötig)
    - verwende Push und Pop, damit Du im aufrufenden Programmteil nicht unbeabsichtigt etwas überschreibst. Erst wenn Du in Deinen Routinen zu Anfang die Inhalte aller verwendeten Register (die NICHT als Eingabeparameter oder Rückgabewerte verwendet werden) auf den Stack pushst, dann veränderst und am Ende in umgekehrter Reihenfolge wieder die Inhalte zurückpoppst, bist Du auf der sicheren Seite.

  4. #4
    Neuer Benutzer Öfters hier
    Registriert seit
    14.08.2015
    Beiträge
    16
    Es tut mir leid für die nervige Fragen.. also ein letztes mal . Ja ich will 80000 Takte. Wie meinst du mit ich muss die inneren loops vier mal durchlaufen bzw 4 als Startwert nehmen?



    vielen vielen Dank

    - - - Aktualisiert - - -

    also warum ich eine dritte schleife noch mit einfügen möchte, weil die mir empfohlen wurde. Ich habe zusätzlich diesen Beitrag aus einem Forum gelesen und dachte es wäre notwendig.

    Beitag aus Forum:

    Also, gesetzt den Fall, ein djnz-Befehl bräuchte nur 1 Taktzyklus. Nehmen wir mal an, die CPU würde mit 4,772727 MHz betrieben (ich weiß gar nicht, wie ich jetzt auf so'n komischen Wert komme...) Dann bedeutet das, sie hat 4772727 Takte pro Sekunde und um 2 Sekunden zu überbrücken, muß man 2 x 4772727 = 9545454 Takte durchlaufen lassen - oder anders ausgedrückt: So oft muß die innere Schleife durchlaufen werden, wenn der Befehl nur 1 Takt verbraucht. Verbraucht er mehr Takte, muß man den Wert durch die Anzahl Takte teilen, wenn er z.B. 3 Takte verbraucht, wäre der "Ticker-Wert" = 3191919 (eben 9545454 / 3).

    Von diesem Wert wird ja rückwärts nach 0 gezählt. Register sind ja immer binär, daher also z.B. 8 oder 16 oder 32 bit breit (ja, es gibt auch 4- oder 12-bit Register...), das heißt, der Wert ist im Falle von 8-Bit-Registern immer nach genau 8 bit zu teilen.
    Wäre er also 9545454, wäre das binär (als 24-bit-Wert) :
    100100011010011011101110, der ist also immer nach 8bit zu teilen:

    10010001 10100110 11101110, oder 145, 166 und 238 - und genau das sind die Werte, die nach R2, R1 und R0 müssen (nach R0, weil das die innerste Schleife ist, muß der unterste Wert, also hier 238, nach R1 müßte 166 und nach R2 dann 145).

    Einfachere Möglichkeit ist, einfach immer durch 256 zu teilen:
    9545454 / 256 = 37286, Rest 238
    32786 / 256 = 145, Rest 166
    (145 / 256 = 0, Rest 145 - aber das braucht man ja nicht mehr zu "berechnen"...)
    Diese Teilungsreste sind dann (sozusagen im Zahlensystem der 256) die einzutragenden Werte.
    (Ja, oder man wandelt die Zahl einfach ins Hexadezimalsystem und nimmt immer 2 Ziffern... Leider gibts heutzutage Assembler-Programmierer, die noch nie was vom Hexadezimalsystem gehört haben, deswegen weiß ich da manchmal nicht, ob ich das wie früher einfach voraussetzen kann...)

    .............

  5. #5
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    07.04.2015
    Beiträge
    903
    Die Verwendung des dritten Registers ist eine Ressourcenverschwendung. Zu Anfang denkt man sich: "Ich hab doch 32 davon (R0..31)". Dann stellt man fest, man kann nur mit den oberen 16 alle Assemblerbefehle nutzen und eigentlich gehen davon auch noch die obersten 6 Register noch einmal ab, weil nur damit die 16-Bit-Adressierungen über X, Y und Z möglich sind.
    Was bleibt, sind magere 10 Registerchen zur vollen Verwendung.

    Zum Abschluss und zum Gedanken anregen:
    Wenn es Dir gelingt, eine 1Mikrosekunde-Warteschleife zu verfassen
    Wait1u:
    //Einsprung 3 Takte über rcall
    nop //10*1
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    ret //Rücksprung 3 Takte
    //16 Takte


    Dann kannst Du doch auch eine Routine über 100 Mikrosekunden verfassen, in der Du die Wait1u-Routine wiederverwendest:
    Wait100u:
    //3 Takte Einsprung über rcall
    push r16 //1
    ldi r16, 84 //1
    Wait100u_Loop:
    rcall Wait1u //16 Takte
    dec r16 //1
    brne Wait100u_Loop //2
    Wait100u_End:
    pop r16 //1
    ret //3
    //19*84 + 9 = 1605


    Dann kannst Du auch nach der gleichen Form eine Wait1m schreiben
    Wait1m:
    //3 Takte Einsprung über rcall
    push r16 //1
    ldi r16, 10 //1
    Wait1m_Loop:
    rcall Wait100u //1605 Takte
    dec r16 //1
    brne Wait1m_Loop //2
    Wait1m_End:
    pop r16 //1
    ret //3
    //1608*10 + 9 = 16089 Takte


    Und ganz zum Schluss
    Wait5m:
    //3 Takte Einsprung über rcall
    push r16 //1
    ldi r16, 5 //1
    Wait5m_Loop:
    rcall Wait1m //16089 Takte
    dec r16 //1
    brne Wait5m_Loop //2
    Wait5m_End:
    pop r16 //1
    ret //3
    //16089*5 + 9 = 80454 Takte

    Diese Routinen (ich hab's jetzt hier im Editor geschrieben, mag noch irgendwo ein Fehlerchen drin sein) sind erweiterbar (Du kannst Dich damit bis in den Stundenbereich hochkaskadieren), anpassbar (Du musst nur die unterste Funktion an die Frequenz anpassen) und einfach wartbar (die Wartefunktionen haben immer die gleiche Form).
    Aber das Wichtigste: das einzige verwendete Register r16 ist in jedem Fall save, weil es in jeder Aufrufebene gepusht und gepoppt wird. Du kannst es also vor dem Aufruf einer der o.g. Wartefunktionen mit einem Wert beschreiben und nach dem Aufruf immer noch sicher sein, dass in r16 noch das steht, was Du dort hineingeschrieben hast.
    Geändert von Holomino (15.08.2015 um 00:08 Uhr)

  6. #6
    Erfahrener Benutzer Robotik Visionär Avatar von Hubert.G
    Registriert seit
    14.10.2006
    Ort
    Pasching OÖ
    Beiträge
    6.220
    Dir ist aber schon klar das in der Wartezeit das komplette Programm steht. Es sind weder Eingaben noch Ausgaben möglich.
    Für diese Aufgaben sind die Timer vorgesehen.
    Grüsse Hubert
    ____________

    Meine Projekte findet ihr auf schorsch.at

  7. #7
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    07.04.2015
    Beiträge
    903
    Zitat Zitat von Hubert.G Beitrag anzeigen
    Dir ist aber schon klar das in der Wartezeit das komplette Programm steht. Es sind weder Eingaben noch Ausgaben möglich.
    Für diese Aufgaben sind die Timer vorgesehen.
    Klares "Jein" dazu.
    Das kommt auf den Anwendungsfall an. Ich persönlich finde es grausam, ein Konstrukt über einen Timer zu entwerfen, dass mir z.B. das Blinken einer einfachen Betriebs-LED im Sekundentakt erlaubt (am Blinken über die Wait-Routine siehst Du sogar, wie Deine IRQ-Auslastung ist).
    Alles in IRQs auzulagern, während das Hauptprogramm sich bei
    Main: jmp Main
    in der Nase bohrt, ist auch Kappes.

    Timer verwendest Du, wenn Du ein Timing (nach oben und nach unten, z.B. UART-Emulation) einhalten willst. Die Verwendung von einfachen Warteschleifen zur Einhaltung von Mindestzeiten (z.B. Tastenentprellen, I2C-Emulation,...) ist meiner Ansicht nach legal.

Ähnliche Themen

  1. Zeitschleife Mikrocontroller
    Von sisi im Forum Bauanleitungen, Schaltungen & Software nach RoboterNetz-Standard
    Antworten: 4
    Letzter Beitrag: 08.10.2015, 20:44
  2. [ERLEDIGT] Zeitschleife zu kurz....ATTiny2313
    Von oderlachs im Forum C - Programmierung (GCC u.a.)
    Antworten: 2
    Letzter Beitrag: 10.03.2013, 15:10
  3. Taster mit Zeitschleife
    Von bnitram im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 15
    Letzter Beitrag: 28.05.2010, 18:01
  4. Zeitschleife
    Von Exodus im Forum AVR Hardwarethemen
    Antworten: 4
    Letzter Beitrag: 07.06.2006, 17:32
  5. Zeitschleife
    Von Exodus im Forum AVR Hardwarethemen
    Antworten: 1
    Letzter Beitrag: 01.06.2006, 14:34

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

LiFePO4 Speicher Test