mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AVR Assembler "Pause nach beliebiger dauer"


Autor: kinglazee (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin ein absoluter Newbie in sachen AVR Assembler, möchte zum anfang
eine art Lauflicht programmieren.
Ich würde gern eine Pause in ein einzelnes Unterprogramm schreiben und
diese immer aufrufen wenn ich sie brauche.
Die Pausen sollte ich in einem Register eintragen können, variieren zw.
10ms und 2000ms.

Wie kann ich das nun realisieren? Ich arbeite mit dem STK500, AVR
Studio V4 und mit einem 8515.
Im AVRStudio bekomme ich angezeigt dass das Board mit einer Frequenz
von 3.69MHz läuft, ob der Wert richtig ist weiß ich aber nicht genau,
wie kann ich das rausfinden?

Bin für jede Hilfe dankbar.
MFG kinglazee

Autor: ...HanneS... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier läuft gerade ein Lauflicht-Thread von Karl. Schau dir das erstmal
an, das hilft bestimmt...

Autor: Sixem (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja die Zeitverzögerung kannst du mit ineinander verschachtelten
Zeitschleifen realisieren.
Oder du nimmst einen Timer.

Den Takt des µC findest du so heraus, indem du einfach den quarz auf
dem board suchst und schaust was draufsteht.

Vieleicht hilft dir das:
;***********LAUFLICHT******************
.include "2313def.inc"    ;include definition files
.org 0x00          ;set Prog.Count. to 0
.def comleft=r19      ;r19 is named to comleft
.def comright=r20      ;r20 is named to comright
.def movevalue=r17      ;r17 is named to movevalue
.def time1=r21        ;r21 is named to time1
.def time2=r22        ;r22 is named to time2
.def time3=r24        ;r23 is named to time3

  rjmp reset        ;jump to reset

reset:
  ldi r16,0xff      ;load value 0xff into r16
  out ddrb,r16      ;set directionregister from port b to output
  ldi r16,ramend      ;init_stackpointer
          ;brauchst du damit du in ein unterprogramm springen kannst
  out spl,r16
  ldi comleft,0b10000000    ;load 0b10000000 into comleft(r17)
  ldi comright,0b00000001    ;load 0b00000001 into comright(r20)
  ldi movevalue,0b00000001  ;load 0b00000001 into movevalue(r17)


left:
  rcall Zeitschleife
  out portb,movevalue    ;put value from movevalue on port b out
  rol movevalue      ;rotate the value from movevalue one stead left
  cpse movevalue,comleft    ;compare if movevalue equals to comleft
          ;if condition is true skip the next command
  rjmp left        ;jump to left
  rjmp right        ;jump to right

right:
  rcall Zeitschleife
  out portb,movevalue      ;put value from movevalue on port b out
  ror movevalue        ;rotate the value from movevalue one stad right
  cpse movevalue,comright      ;compare if movevalue equals to comright
            ;if condition is true skip the next command
  rjmp right
  rjmp left


Zeitschleife:          ;das ist dein Unterprogramm.
  ldi time3,0xff

pos2:
  Ldi time2,0xff

pos1:
  ldi time1,0x20
pos:
  dec time1        ;3 ineinander verschachtelte Zeitschleifen
  brne pos        ;schleife wird 255x255x20 mal durchlaufen
  dec time2        ;allerdings musst du noch darauf achten wieviele 
clocks
der prozessor für die
            ;einzelnen befehle braucht.
  brne pos1
  dec time3
  brne pos2
  ret

Autor: Sixem (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die initalisierung des stackpointers ist allerdings nicht bei allen µcs
gleich..


beim 8515 müsste es so gehen.

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

aber schau lieber nochmal im datenblatt nach... dort steht es drinnen.

Autor: kinglazee (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich habe mir den Thread von Karl mal durchgelesen, werde aber
leider nicht direkt schlau drauß, vielleicht muss ich mich damit noch
intensiver beschäftigen.
Aber ich haben unten mal mein Programm kopiert, wie gesagt, bin newbie
und es soll nur zur übung sein.
Kann mir jemand sagen was ich jetzt tun muss um z.b. eine Pause von
250ms bei einem Takt von 3,69MHz in das Unterprogramm "Pause" zu
bekommen?

P.S. So läuft mein Programm, ich habe ins Unterprogramm Pause einfach
jede menge "NOP"'s eingefügt um zu sehen obs hinhaut. aber das ist
sicherlich keine gute Lösung.


.NOLIST
.INCLUDE "8515def.inc"
.LIST
.DEF  mp = R16
  rjmp  main

main:
  ;Initiate Stackpointer
  ldi  mp,LOW(RAMEND)
  out  SPL,mp
  ldi  mp,HIGH(RAMEND)
  out  SPH,mp

  ;Port D für lesen vorbereiten
  ldi  mp,0x00 ; 8 Nullen in Universalregister
  out  DDRD,mp ; an Datenrichtungsregister
  ;Pull-Up's reinschalten (1 bei Tastendruck)
  ldi  mp,0xFF ; 8 Einsen in Universalregister
  out  PORTD,mp ; und an Port D (das sind jetzt die Pull-Ups!)
  ;LED's ausschalten
  ldi  mp,0xFF ; 8 Einsen in Universalregister
  out  DDRB,mp ; und in Datenrichtungsregister(1=Ausgehend)
  out  PORTB,mp ; und an alle Ausgänge (1=Aus)

  rjmp lauflicht

lauflicht:

  cbi portb,0
  rcall pause
  sbi portb,0

  cbi portb,1
  rcall pause
  sbi portb,1

  cbi portb,2
  rcall pause
  sbi portb,2

  cbi portb,3
  rcall pause
  sbi portb,3

  cbi portb,4
  rcall pause
  sbi portb,4

  cbi portb,5
  rcall pause
  sbi portb,5

  cbi portb,6
  rcall pause
  sbi portb,6

  cbi portb,7
  rcall pause
  sbi portb,7

  rjmp lauflicht

pause: ;Unterprogramm für Wartezeit


        ret

Autor: ...HanneS... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das sind mehrere Threads, hast du die alle schon durch?

Blättere mal einige Tage zurück, dann findest du sie. Im ersten ging es
wohl um ein "ganz besonderes Lauflicht".

Frohen Rest vom Fest...

Autor: ...HanneS... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: kinglazee (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

also ich bin wirklich erstaunt was sich innerhalb von einem Tag in
diesem Thread alles getan hat, Leute ich bin in einigen Foren (viele
versch. Themen) aber ich habe selten eins gesehen das so gut besucht
ist und man so schnell sämtliche Hilfestellungen bekommt.
DICKES FETTES LOB an euch...!

So jetzt zum Thema:
Ich habe mir alle geposteten Threads durchgelesen, nur ich glaube dass
ich noch ein zu großer Anfänger bin um damit was anfangen zu können.
Wäre es zuviel verlangt mir ein Stück Source Code anzupassen damit ich
es einfach reinkopieren kann?
Wäre wirklich sehr dankbar.
Wie das ganze dann Funktioniert werde ich mir aus meinen Befehlen
rauslesen und genau nachschauen warum was gemacht wird.

MFG kinglazee

Autor: ...HanneS... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tja, es hat sich viel getan hier. Es scheint hier also nicht nur
(königliche) Faulpelze zu geben.
Aber Lesen und Verstehen musst du schon selber...

...HanneS...

Autor: kinglazee (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wäre auch dankbar wenn mir jemand ein Tutorial zeigen könnte in dem
Wirklich genau die Timerfunktion erklärt ist, habe mittlerweile schon
viel gegoogelt und nichts gefunden!

MFG kinglazee

Autor: ...HanneS... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dazu reicht das Datenblatt...

Autor: kinglazee (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, ich habe mal weiter gesucht, bin mal auf diese Seite gestoßen:
http://www.mc-project.de/Pages/home.html
--> Timer

Ich finde das tutorial sehr gut, aber leider ist das C++ und davon hab
ich auch noch keinen Plan. Könnte jemand den Quelltext übersetzen oder
mir einen Link geben in dem man weiteres nachlesen kann?

Ich will doch nur eine billige Zeitverzögerung, kann ja so schwer ned
sein, ich schaff nur den Sprung in diese Programmiersprache nicht so
richtig.

Ich hoffe ihr könnt mir helfen...

Autor: ...HanneS... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Quelltext übersetzen?

Ich habe mir die Seite angesehen und finde, dass sie die Funktion des
Timer-Überlauf Interrups gut erklärt.

Im Prinzip muss man ja nur im I/O-Bereich einige Bits in den Registern
setzen bzw. einige Werte in Register schreiben. Welche Register das
sind, ist von AVT-Typ zu AVR-Typ leicht unterschiedlich und muss
komkret im Datenblatt nachgeschlagen werden.

Was ist nun dieser "I/O-Bereich"??
Dazu müsste man erstmal die dementsprechenden Adressbereiche
auseinander halten können um zu verstehen, was mit I/O-Bereich gemeint
ist.

Denn es gibt im AVR:
- Flash-Speicher
- EEPROM-Speicher
- 16 "eingeschränkt nutzbare" Register
- 16 "vollwertige" Register
- 32 "vollwertige" I/O-Register
- 32 "eingeschränkt nutzbare" I/O-Register
- SRAM

Nun recherchiere erstmal, was ich damit meine, ohne die dabei
gewonnenen Erkenntnisse hat es keinen Sinn, weiter zu machen, denn dann
kannst du den Rest ja nicht verstehen... Das wäre ja so, als wolle man
ein Buch schreiben, ohne das Alfabet zu kennen.

Im nächsten Schritt kannst du ergründen:
Wie kann man nun in Assembler den I/O-Bereich eines AVR ansprechen?

Dazu gibt es Assembler-Befehle. Diese sind am Ende des Datenblattes in
einer Tabelle zusammengefasst. Man sollte sich die Tabelle schon mal
ausdrucken und da hin hängen, wo man sich oft aufhält, denn von alleine
lernt (und versteht!!!) sich die nicht.

Da wirst du unter Anderem auf folgende Befehle stoßen:
in, out, sbi, cbi - Mit diesen kann man auf I/O zugreifen.

Nun musst du noch das Register, das du nach I/O kopieren willst, mit
einem bestimmten Wert (nennt man das "Konstante"?) laden. Dazu dienen
entsprechende Befehle wie ldi, clr, ser... Man kann aber auch einzelne
Bits der Register verändern: andi, ori, and, or, eor, sbr, cbr...

Um nun einen Timer-Überlauf-Int zu programmieren, muss man nun (in ASM)
Folgendes tun:
- Include-Datei einbinden
- Reset-Vektor auf Reset-Routine setzen
- Interrupt-Vektoren auf die ISR's (Interrupt-Service-Routinen)
  setzen
- Reset-Routine schreiben (mit Label beginnen)
  in Reset-Routine:
  - Stackpointer initialisieren
  - genutzte Ports initialisieren
  - Timer-Vorteiler setzen (und damit Timer einschalten) (tccr0)
  - Timer-Startwert definieren und setzen (tcnt0)
  - Timer-Überlauf-Interrupt-Maske setzen (timsk)
  - Interrupt global freischalten (sei)
- Ein "Hauptprogramm" schreiben, in dem (vorerst) eigentlich nix
  gemacht wird: "main: rjmp main"
- Eine Timer-ISR schreiben, die Folgendes macht:
  - SREG sichern
  - Timer-Startwert ausgeben (tcnt0)
  - Ein oder mehrere Register hoch(oder runter)zählen, mit denen
    du die Anzahl der Interrupts zählst und damit Zeiten zählen
    kannst.
  - In Abhängigkeit der Zählregister bestimmte Arbeiten ausführen
    oder eben nicht (man kann bei genügend großem Int-Abstand sogar
    das gesamte Hauptprogramm in der Timer-ISR ausführen).
  - Das SREG wiederherstellen
  - Die ISR mit RETI beenden

Beispiele dazu findest du in den weiter oben genannten Links.
Lesen und verstehen solltest du das aber schon selbst.
Konkrete Fragen beantworte ich dir gerne, allgemeine Fragen jetzt nicht
mehr.

Guten Rutsch...
...HanneS...

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.