Forum: Projekte & Code Warte-Befehl in Assembler?


von Sebastian Wille (Gast)


Lesenswert?

Hallo zusammen,

ich brauche einen Befehl für mein Assemblerprogramm, der das Programm 
für eine definierte Zeit (z.B. 500ms) anhält. (Ein Relais soll für eine 
kurze Zeit anziehen und wieder loslassen.)

Oder mache ich das über einen Timer? Gibt es eine Art Delay-Befehl 
(nicht den nop)?

Danke im voraus! :-)

Sebastian

von Fritz 7 (Gast)


Lesenswert?

Also mir ist kein solcher Befehl bekannt. Du kannst entweder den Delay 
Schleifen generator(irgendwo zu downloaden, such in den 
Beiträgen)verwenden oder den Timer. Beides ist ziemlich einfach. Um nur 
schnell etwas mit dem Relais zu machen ist der Schleifengenerator ideal. 
Man kan genau eingeben wie lange gewartet werden soll und der Code wird 
generiert.


Fritz 7

von Rainer (Gast)


Lesenswert?

Wenn du während des Wartens was anderes zu tun hast bzw ein Interrput 
oder so ausgelöst werden könnte und die Zeit eher kritisch ist, ist die 
Lösung über den Timer weitaus eleganter als den Prozessor nop'en zu 
lassen, bis die 500ms vorbei sind (denn was anderes zum warten gibts 
eigentlich nicht).

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?


von Sebastian Wille (Gast)


Lesenswert?

Hi,

danke für den Link. Allerdings geht es da ja meistens um einen 
Generator, der den Code erzeugt (die meistens Links sind defekt). Ist es 
denn wirklich so schwer? Gibt es nicht ein paar gut kommentierte Zeilen, 
daß ich auch weiß, was ich mache?

Kennt noch jemand einen funktionierenden Link zu einem Generator?

Danke!

Sebastian

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Der Generator baut dir einfach aus ein paar Befehlen (rjmp, cmp, inc...) 
eine Schleife mit der gewünschten Länge zusammen. Natürlich kann man das 
auch von Hand machen, nur wozu?
http://www.home.unix-ag.org/tjabo/avr/AVRdelayloop.html

von Holger F (Gast)


Lesenswert?

so als perfektionist: was mache ich denn wenn ich es ganz genau haben 
will? soweit ich das ueberblickt habe, ist in dem generierten code die 
zeit, die die schleifenverwaltung benötigt nicht mit einbezogen. da 
stellt sich natürlich die frage: war ich nur unfähig die anleitung zu 
lesen oder fehlt da eine liste mit der zyklenanzahl, die pro befehl 
benötigt werden?

antworten werden dankbar entgegengenommen...
Holger

von Markus (Gast)


Lesenswert?

Ich denke schon das die Takte für die Sprungbefehle usw mit eingerechnet 
werden. Ansonsten bekommt man ja völlig falsche Werte.
Die Genauigkeit der Zeitmessung hängt auch mit der Taktfrequenz 
zusammen.

von Schmittchen (Gast)


Lesenswert?

> Ich denke schon das die Takte..
Und ich weiß es. (Hab nachgesehen).

> Die Genauigkeit der Zeitmessung hängt auch mit der Taktfrequenz zusammen.
Nein. Vielleicht meinst du die Auflösung - die Genauigkeit jedenfalls 
hängt nicht mit der Taktfrequenz zusammen.

Schmittchen.

von holger f (Gast)


Lesenswert?

jo, jetzt hab ich das auch kapiert, stimmt also wirklich genau. und hab 
auch inzwischen die zyklenanzahlen gefunden... instructions.pdf sei 
dank...

von Peter Kutsch (Gast)


Angehängte Dateien:

Lesenswert?

Hi
Also mit den Schleifen beim AVR ist das so eine Sache. Als Beispiel sei 
die RC5 Fernbedienung genannt. Wenn man den Anhang anschaut, so ist das 
eine simpelst Geschichte bei der C-Control von Conrad. Programmiert man 
das Gleiche beim AVR, so erhält man alles, nur keine RC5 Befehle. Wenn 
man sich dann den generierten Code von Compilern (Bascom etc.) oder was 
zu RC5 an Codebeispielen zum AVR anschaut, so stellt man schnell fest, 
dass hier mit ober und unter-limits gearbeitet wird. Warum, wenn die 
Befehle immer die gleiche Zeit benötigen?
Also ich habe meine absoluten Zweifel an Schleifen oder auch Timer 
Routinen beim AVR
Peter

von Markus (Gast)


Lesenswert?

Hi Peter,
ehrlich gesagt versteh ich das nicht so ganz was Du meinst. Wieso soll 
es Probleme geben mit Delays oder Schleifen beim AVR?

Gruß
Markus

von Peter Kutsch (Gast)


Angehängte Dateien:

Lesenswert?

Hi Markus
Ich habe jetzt mal das umgeschriebene Prog für AVR angehängt. Ich weis 
zwar nicht mehr, auf welchem Versuchsstand das Prog ist, aber wenn du 
die beiden Progs vergleichst, siehst du, dass sie das gleiche machen. 
Der Unterschied, mal von Kleinigkeiten abgesehen, besteht darin, das 
Prog für C-Control funzt, das Prog für AVR funzt nicht. Ich habe dann 
das Prog für den AVR in alle möglichen Richtungen geändert, um zu 
erfahren, wie lange ein Biphasenslot dauert (mit Schleifen). Es kam nur 
selten der gleiche Schleifenzähler dabei raus. Wenn du dir dann mal die 
funktionierenden Progs für den AVR anschaust, siehst du, dass diese mit 
Zeitunter- und Zeitobergrenzen für einen Biphasenslot arbeiten. Ich habe 
jedenfalls kein funktionierendes RC5 Prog für den AVR gefunden, dass wie 
bei der C-Control mit festen Schleifenzählern läuft.
lg
Peter

von Markus (Gast)


Lesenswert?

Hi Peter,
kenn mich mit RC5 nicht aus. Weiß auch nicht was mit Bipahsenlot gemeint 
ist.
Aber ich sehe trotzdem keinen Grund warum man mit dem AVR keine 
Schleifen oder sowas machen können soll. Vielleicht liegt es einfach 
daran das der AVR um einiges schneller ist als die C-Control und daher 
bestimmte Programmiertechniken, die mit der C-Control noch 
funktionieren, mit dem AVR nicht mehr möglich sind. Deshalb evt die von 
Dir angesprochene Lösung mit den Ober- und Untergrenzen. Vielleicht um 
Signalungenauigkeiten, die die C-Control noch nicht registrieren würde, 
auszugleichen.

Gruß
Markus

von Schmittchen (Gast)


Lesenswert?

> Der Unterschied [...] besteht darin, das Prog für C-Control funzt, das Prog für 
AVR funzt nicht

Aha, und den Grund dafür suchst du dann am AVR? Seltsame Logik.
Im Netz gibts genug RC5-Beispiele für AVRs. Die meisten bauen auf Peter 
Fleurys Implementation auf.
<http://www.mysunrise.ch/users/pfleury/avr-software.html>;

Das Problem bei der Fernbedienungssoftware ist nicht AVR oder RC5, 
sondern daß nicht alle Hersteller sich streng an RC5-Specs halten. 
(Viele Fernbedienungen benutzen auch andere, RC5-inkompatible Protokolle 
- das nur nebenbei). Oftmals wird auch der EmpfägerIC (TSOP17xx) mit der 
falschen Frequenz eingesetzt.

Das gleiche Problem sehe ich übrigens beim Empfang/Auswertung der 
DCF77-Signale. Wirklich idiotensichere AVR-Software dazu gibt es 
nicht... aber auch den AVR als solchen würde ich das nicht schieben.

Schmittchen.

von Peter Kutsch (Gast)


Lesenswert?

Hi
erklärt mir doch einmal, was an meiner Logik falsch ist. Ein Bit dauert 
Biphasencodiert 1778 us. Bei 8MHz dauert ein Takt 0,000000125 s. 1778us 
= 0,001778 s. 0,001778 / 0,000000125 = 14224. Wenn nun das 
Biphasensignal in 4 Teile zerlegt wird, dann 14224 / 4 = 3556 Takte bis 
zur Abfrage des Pegels. Bei dem AVR vergehen bei mir zwischen 11000 und 
17000 Takten pro Bit. Bei der C-Control ist die Anzahl der Takte immer 
gleich.
Wie gesagt, ich möchte den AVR nicht madig machen. Wenn ich den nicht 
gut finden würde, dann würde ich ihn nicht nutzen. Tue ich aber. Ich 
warne nur vor Schleifenzählern. Möglicherweise treibt der AVR durch INTs 
etc. andere Dinge die Zeit brauchen. Dann ist mein Schleifenzähler 
makulatur.
Was lernt man aus der von Schmittchen angegebenen Referenz?
if ( (timer > RC5BITREF1) && (timer < RC5BITREF2) ) {
        // startbit ok, decode
Nun, es wird ein Zeitslot für die Codierung verwendet, also kein fester 
Zeitwert wie bei der C-Control. Allein die Ermittlung des Zeitslots ist 
fast so aufwendig, wie der gesamte RC5-Code der C-Control. Das heist 
meiner Meinung nach aber nicht, dass die C-Control dem AVR überlegen 
ist.
lg
Peter

von Schmittchen (Gast)


Lesenswert?

Vorweg: Mein "Peter-Fleury-Link" war leider nicht der, den ich zu diesem 
Thema bringen wollte (hab mich in meinen Bookmarks verirrt). Eigentlich 
wollte ich auf Bray verweisen:
http://bray.velenje.cx/avr/
Und der verwendet einen Interrupteingang und den Timerinterrupt zur 
Dekodierung.

> Bei dem AVR vergehen bei mir zwischen 11000 und 17000 Takten pro Bit.
Nein, sondern genau 14224, wie du auch oben ausgerechnet hast (@8MHz). 
Nochmal: 1Bit ist 1778us lang. Bei 8MHz (=125ns) werden genau 14424 
Takte ausgeführt. Das was du beobachtet hast, war wahrscheinlich bei 
einer AVR-RC5-Version, die nicht auf die fallende Flanke des 
IR-Empfängerausgangssignal reagiert. So können in deinem Falle noch alle 
möglichen Interrupts dazwischenkommen bis das Hauptprogramm per Polling 
merkt, daß da was ansteht. Oder je nach Zustand des AVRs/ext.Hardware 
kann die Ausführung einzelner Hauptprogrammteile unterschiedlich viel 
Zeit in Anspruch nehmen. Das kann dann zu den 11000-17000 Takten führen.
Aber das hast du ja schon selbst bemerkt:
> Möglicherweise treibt der AVR durch INTs etc. andere Dinge die Zeit brauchen...

Wie RC5 am besten dekodiert wird, beschreibt Ralf Spettel auf seiner 
Seite sehr anschaulich:
http://www.spettel.de/ralf/index_reload.html?http://www.spettel.de/ralf/projekte/avr_rc5/index.shtml

Zieh dir die Seite mal rein, dann dürften auch deine Bedenken/Probleme 
bzgl. unterschiedlicher Taktzahlen im Prinzip geklärt sein.

> Was lernt man aus der von Schmittchen angegebenen Referenz?
Wie mans besser nicht machen sollte. ;-) oder besser :-(

Können bei der C-Control keine "unvorgesehenen" Dinge (wie z.B. INTs 
oder je nach Situation zeitlich unterschiedlich lange Programmteile) 
auftreten? Das hieße dann ja, daß während RC5 dekodiert wird, nichts 
anderes ausgeführt werden kann?
(Anm.: Ich kenne die C-Control nicht).

Schmittchen.

von Peter Kutsch (Gast)


Lesenswert?

Hallo Schmittchen
Erst mal danke für die links. ich denke, die sind für alle interessant. 
weiter denke ich, dass wir den disput hier beenden können, denn mit 
deinem satz
>Oder je nach Zustand des AVRs/ext.Hardware kann die Ausführung einzelner 
Hauptprogrammteile unterschiedlich viel Zeit in Anspruch nehmen. Das kann dann zu 
den 11000-17000 Takten führen.
Aber das hast du ja schon selbst bemerkt:<
haben wir ja doch noch einigkeit erzielt. ursprünglich wollte ich ja 
lediglich zur vorsicht bei der verwendung von schleifen mit 
schleifenzählern warnen. die rc5 problematik war nur ein beispiel zur 
verdeutlichung. viele standartroutinen zur zeitvertrödelung benutzen 
aber genau diesen schleifentyp. auch delay.zip auf der homepage von bray 
(http://bray.velenje.cx/avr/) verwendet diesen typ. dein zweiter link 
ist ebenfalls sehr hilfreich in der angelegenheit. er besagt, grob 
vereinfacht, vertraue nicht auf einen zähler, sondern synchronisiere 
dich mit hilfe von steigenden oder fallenden flanken. er weist auch 
darauf hin, dass die methode von bray problematisch sein kann. kann, 
nicht muss.
lg
peter

von Uwe (Gast)


Lesenswert?

Hi
Einen kleinen Einwurf habe ich noch. Wiso gibt es keine Idiotensichere 
SW für DCF77, wie meinst du das Schmittchen? Erst solltest du testen und 
dann drüber herfallen.
Schleifen benutze ich übrigens nur um eine min. benötigte Zeit zu 
überbrücken, niemals aber um Zeiten zu messen,(naja fast nie) wozu sind 
denn sonst Timer da.
Gruss Uwe

von Peter Kutsch (Gast)


Lesenswert?

Hallo Uwe
als ich die implementation der rc5 routine gestartet habe, war ich noch 
neu beim avr. was lag da näher, als mit schleifen zu arbeiten. der timer 
kam erst später. wenn man neu an einem prozessor rangeht, fängt man halt 
mit dem leichten (schleifen) an. wenn das dann schon nicht klappt, naja 
dann kann es sein, dass einem das selbstvertrauen zur 
timerprogrammierung fehlt. ging mir jedenfalls so.
lg
peter

von Schmittchen (Gast)


Lesenswert?

> Wiso gibt es keine Idiotensichere SW für DCF77
Ich schrieb: "Probleme beim Empfang/Auswertung [...]. Idiotensichere 
Software gibts nicht..."
Naturbedingt ist das DCF77-Signal nicht immer frei von Störungen und der 
Empfang ist auch nicht immer perfekt. Wenn man den TTL-Ausgang der 
gängigen DCF-Empfänger betrachtet, so erscheint das eigentlich immer 
sauber. Timingkritisch sollte die DCF-Kodierung im Gegensatz zu RC5 auch 
nicht sein. Dennoch schaffen es die meisten Softwareimplementationen 
nicht daraus die richtige Zeit zu erzeugen. Die meisten im Netz 
verfügbaren Sourcen bauen auf Volker Oths Code auf. Er verzichtet auf 
Plausibilitätsprüfungen, die meiner Meinung nach bei schlechtem Empfang 
wichtig sind. Z.B. wenn eben noch der 25.Aug war, dann kann in 5min 
nicht der 23.Aug sein usw.. Aber dann besteht ja auch das Problem, daß 
man anfangs eine verläßliche Zeit/Datumsangabe braucht, um solche 
Prüfungen durchzuführen.
Zusammenfassend: Das Problem liegt im (teilweise) schlechten Empfang mit 
fehlender Plausibilitätskontrolle. Eine Software, die man irgendwo 
hernimmt, wie sie z.B. auf jeder AVR-Seite für LCDs bereitsteht, gibts 
für DCF77 ((noch)) nicht.

Vom Gegenteil lasse ich mich natürlich gern überzeugen. Ich muß 
allerdings auch dazusagen, daß seit meinen Erfahrungen inzwischen über 
ein Jahr vergangen ist.

Schmittchen.

von Uwe (Gast)


Lesenswert?

Hi!
Dann schaue dir bitte "DCF77-Uhr mit LCD" in der Codesammlung an, dann 
reden wir nochmal.
Gruss Uwe

von Schmittchen (Gast)


Lesenswert?

@Uwe: So wie ich deinen Assemblercode lese hast du auch keine 
Plausibilitätsprüfungen integriert (habe den für mich schwer lesbaren 
Ass.Code nur überflogen, falls doch implementiert, bitte nicht schlagen 
:-)). Und das Problem mit der Anfangsuhrzeit bleibt auch bestehen - 
irgendwann braucht man eine Uhrzeit, für die man sicher sein kann, daß 
sie stimmt, sonst sind alle Plausibilitätskontrollen unnütz. Dreimalige 
"gültige", aufeinanderfolgende Uhrzeit könnte dafür reichen. (Bei mir 
dauert das dann ca. 30min - 3h, bis 3x gültige Uhrzeit kommt; mit Volker 
Oths C-Code).

Allgemein: Die Uhrzeit der Uhr per Taster (oder sonstwie) vorsagen, 
finde ich am Ziel einer Funkuhr vorbei.
Ich habe hier auch eine gekaufte fertige Funkuhr (mit Funkthermometer), 
die auch manchmal die falsche Zeit anzeigt. Meist werden die Stunden als 
0 erkannt. Dann ist um 16:54 -> 0:54.
Ich bin hier ca. 150km südwestlich von Frankfurt. Zwischen Frankfurt und 
der Funkuhr (auch dem Modul) stehen noch ein paar Bildschirme und die 
Gebäudeaußenwand - evtl. wirkt sich das auf meinen schlechten Empfang 
aus?

Schmittchen.

von Uwe (Gast)


Lesenswert?

Hi
Habe leider momentan etwas wenig Zeit.
Ich mache einen Quersummentest zwischen alter und neuer Zeit. Da sich 
pro Übertragung meistens nur die Minute ändert ist das sehr 
wirkungsvoll.
Will sagen: alte Zeit + 1 mit neue Zeit muss gleich sein, sonst war es 
fehlerhaft. Damit konnte ich bis jetzt alle fehlerhaften Zeichen 
verhindern. Das Ding läuft bei mir als Schaltuhr und da wären falsche 
Zeiten fatal.
Ich mache es noch mal optisch:
12:20 30.8.02 Quersumme = 18+1 = 19
12:21 30.8.02 Quersumme = 19
Wenn das OK ist kann die Zeit synchronisiert werden.
Es wird automatisch ein Fehler bei jedem Übergang der Minutenzehner 
erzeugt (19->20 Q1=3 Q2=2)
Für 1.Synchr.braucht man, wenn Sign.OK 2 Minuten, wenn Sign. Müll halt 
länger. Ich habe schon mal ne 1/2h gewartet.
Wie gesagt die Sache ist sehr sicher.
Gruss Uwe

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ist zwar schon etwas her dieser Beitrag, aber vielleicht schaut einer 
von Euch doch nochmal vorbei.


Hallo Sebastian,

wie schon gesagt wurde, kann man Wartezeiten sehr gut und sehr genau 
über Zählschleifen realisieren, jedoch mit 2 Nachteilen:

1. man kann dabei nichts anderes machen.
2. wenn die maximale Zeit auch eingehalten werden soll, müssen alle 
Interrupts gesperrt sein.

Deshalb macht man das üblicher Weise nur bei sehr kurzen Zeiten. Z.B. 
benötigt meine 1-Wire-Routine (z.B. für die Maxim DS18B20 
Temperatursensoren) Zeiten von 15µs und 60µs.

Bei Deiner Anwendung kommt es warscheinlich nicht darauf an, ob es 500 
oder 600ms sind. Da würde ich dann einen Timerinterrupt nehmen, der 
einen Takt von etwa 100ms erzeugt. Und wenn das Relais eingeschaltet 
wurde, wird ein Register auf 6 gesetzt und der Timerinterrupt zählt es 
runter und schaltet das Relais ab, wenn der Wert 0 erreicht wurde.


Hallo Peter,

daß das AVR-Programm nicht funktioniert liegt bestimmt nicht an der 
Delay-Routine sondern daß es eben doch nicht gleich ist.
Z.B. ist mir sofort ins Auge gesprungen, daß das SREG nicht gesichert 
wird, es ist doch aber ein Interrupt (endet mit RETI).
D.h. Dein Hauptprogramm hat eigentlich gar keine andere Chance, als 
gegen den Baum zu laufen.

Außerdem springst Du immer zum Ende (rjmp zurueck), das C-control aber 
zum Anfang (brset 0,PC,rc5).


RC5-Dekodierung:

Der RC5-Kode synchronisiert sich ja bei jedem Bit neu. Damit sind exakte 
Zeiten völlig unnötig. Abweichungen von 20% sind kein Problem. Man kann 
deshalb sogar ohne Quarz arbeiten und z.B. den Tiny15 nehmen.
Der Trick ist natürlich, man synchronisiert bei jedem Bit und nicht nur 
beim Startbit.
Mein Beispiel könnt Ihr im Anhang finden.

Kurz zum Prinzip:
Es wird mit dem Timerinterrupt die Zeit bis zum nächsten Pegelwechsel 
gemessen. Der Bittakt ist ja etwa 1,8ms.
Alles kleiner als 0,75 * Bittakt ist nur der zusätzliche Puls, wenn das 
gleiche Bit nochmal kommt.
Alles größer 1,25 * Bittakt ist ne Störung.
Alles dazwischen ist dann das Bit. Der Pegel nach dem Wechsel entspricht 
dem Bitwert und wird in das Ergebnis reingeschoben.
Das ganze 13 mal und fertig.
Falls noch weitere Fragen sind, fragt ruhig.


DCF77-Dekodierung:

also meine Routine ist 100%-ig störsicher (das war doch wohl gemeint mit 
"idiotensicher").
Mir ist jedenfalls noch nie eine falsche Zeit untergekommen.
Mann muß natürlich trotzdem noch eine normale Digitaluhr programmieren. 
Z.B. solange der Fernseher oder der PC-Monitor an sind, empfängt meine 
Routine absolut nichts.
Ich benutze den Conrad-Empfangsmodul.

Die Routine ist auf meiner Webseite:

http://www.specs.de/~danni/appl/soft/c51/thclock/index.htm

Die einzelnen Zeiten messe ich mit einem Timerinterrupt von 64Hz.
Dann ergeben sich folgende Zählerwerte:

0-Bit: 6 (6,4)
1-Bit: 12 (12,8)
Pulsabstand: 64
Synchronimpulsabstand: 128

Das ganze wird also in ein Byte gezählt und dann getestet:

Puls < 5: Fehler
4 < Puls < 10: 0-Bit
10: Fehler
10 < Puls < 20: 1-Bit
Puls > 20: Fehler

Pulsabstand < 60: Fehler
60 < Pulsabstand < 70: O.K.
70 < Pulsabstand < 120: Fehler
120 < Pulsabstand < 140: Synchronimpuls
Pulsabstand > 140: Fehler

Zusätzlich werden noch die 3 Paritätsbits geprüft und ob es auch genau 
59 Impulse waren.

Alle diese Tests sind nur Byte-Vergleiche, d.h. der Rechenaufwand ist 
absolut minimal.

Dafür spare ich ne Menge Code bei der Auswertung:

Üblicher Weise sieht man oft riesige Programme, die jeden Impuls extra 
behandeln.
Aber es wird eigentlich immer nur das gleiche gemacht: Es wird ein Wert 
(1,2,4,8,10,20,40,80) zu einer Speicherstelle addiert. Es gibt also nur 
eine kleine Tabelle, in der steht, welcher Wert auf welcher Adresse 
addiert werden muß.
Und immer, wenn eine 1 addiert wird, wird vorher gelöscht.
Und der Tabellenwert FF bedeutet, daß die Parität geprüft wird.
Das ganze sind dann gerade mal lächerliche 250Byte an Code.


Ich würde mich über Rückmeldungen freuen, d.h. ob Ihr mit Routinen nach 
diesem Prinzip immer noch diese Probleme habt.


Peter

von Peter Kutsch (Gast)


Lesenswert?

Hallo Peter
>>daß das AVR-Programm nicht funktioniert liegt bestimmt nicht an >>der 
Delay-Routine sondern daß es eben doch nicht gleich ist.
>>Z.B. ist mir sofort ins Auge gesprungen, daß das SREG nicht >>gesichert wird, es 
ist doch aber ein Interrupt (endet mit RETI).
>>D.h. Dein Hauptprogramm hat eigentlich gar keine andere Chance, >>als gegen den 
Baum zu laufen.
Wie ich schon schrieb, handelt es sich um einen Auszug des Programmes. 
Weiter schrieb ich, dass ich nicht mehr weis, welcher Stand der vielen 
Testversuche es ist. Aber egal. Tatsache ist, dass man mit Zeitschleifen 
über vertrödeln mit "nop" oder ähnliches keine genauen Zeiten hinkriegt. 
Versuche doch mal bitte die Routine von der C-Control in AVR (AT90S*) 
Assembler zu programmieren. Ich wünsche viel Freude.
Weiter mußte ich feststellen, dass auch der Timer nicht besonders genau 
ist. Beispiel:
Die Routine von Volker Oth zum DCF77 Signal prüft bei einem AVR mit 8 
MHz und eingestelltem Teiler von 1024 mit dem Timer 1 und INT1 bei der 0 
auf Timercounter 1 kleiner 1171, bei einer 1 auf kleiner 1953 und beim 
Startbit auf größer 11718. Bei mir wurde gemessen:
Für eine 0 zwischen 600 und 960
Für eine 1 zwischen 1300 und 1600
Für das Startbit 14000 und 15500
also bei zwei Sekunden ungenau auf etwa 200 ms. Bei Zeitkritischen 
Sachen wohl etwas zu ungenau.
Frage mich bitte nicht, woran das liegt. Die Funkuhr von Volker Oth 
funktioniert einwandfrei. Auch bei laufendem PC und laufendem Fernseher. 
Es sollte aber klar sein, dass Zeitschleifen ohne Sync über ein externes 
Signal problematisch sind.
Welchen Grund das hat, ob z.B. die Taktfrequenz des Quarzes schwankt, 
oder bei Schaltvorgängen die Spannung kurz zusammenbricht, oder, oder, 
oder, muß man wohl von Fall zu Fall untersuchen.
lg
Peter

von Peter D. (peda)


Lesenswert?

Hallo Peter,

es gibt überhaupt keinen Grund, warum Verzögerungsschleifen oder Timer 
nicht genau sein sollen. Bei einem 2313 mit 10MHZ sind sie auf 100ns 
genau. Und die Quarze schwingen auch auf mindestens 6 Stellen genau.

Wenn Du aber analoge Signale messen willst, können diese natürlich 
erheblich schwanken.
Z.B. durch wechselnde Empfangssignalstärke und auch durch die Tiefpässe 
zum Ausfiltern der Trägerfrequenz kriegst Du automatisch erhebliche 
Schwankungen draufaddiert.

Deshalb muß man bei Signalen aus der analogen Welt immer mit unteren und 
oberen Schwellen arbeiten.

Wenn Du dagegen mit einem 2. AVR die Impulse erzeugen würdest, kriegst 
Du auch immer ein konstantes Meßergebnis geliefert.


Die Programme habe ich mir nur auf Unterschiede und Fehler hin 
angesehen.
Es macht ja auch keinen Sinn, ein Assemblerprogramm zu schreiben, wenn 
ich schon ein super funktionierendes C-Programm fix und fertig habe.


Peter

von Peter Kutsch (Gast)


Lesenswert?

Hallo Peter
warum hat die C-Control die Probleme nicht?
lg
Peter

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Welche Probleme? Bitte ein konkretes _Minimal_-Beispiel.

Die Ausführungszeiten der Befehle sind im Instruction Set genau 
angegeben, z.B. bei sbrc:
| Cycles:
| 1 if condition is false (no skip)
| 2 if condition is true (skip is executed) and the instruction skipped is 1 word
| 3 if condition is true (skip is executed) and the instruction skipped is 2 words

Bei welchem Befehl ist diese Angabe falsch?

von Peter Kutsch (Gast)


Lesenswert?

Hallo Andreas
Ich weis nicht, ob Du mich meinst. Ich habe nicht behauptet, dass die 
Angabe der Anzahl der Takte pro Befehl falsch ist. Ich habe behauptet, 
dass die Ausführung von Schleifen unterschiedlich lange dauern kann. 
Auch der Timer 1 liefert unterschiedlich Timercounts bei der DCF77 Uhr 
von Volker Oth. Ich denke weiter, dass das Thema nun ausgereizt und 
ausdiskutiert ist. Wer glaubt, man könne den Werten trauen, der soll sie 
halt nutzen. Ich jedenfalls traue ihnen nicht mehr von 12:00 bis Mittag. 
Wer meint ich wäre im Unrecht, naja der soll mir eine funktionierende 
Umsetzung der C-Control Routine zeigen. Die ist wie gesagt winzig und 
funktioniert. Alle RC5 Programme des AVR sind wahre Monster im Vergleich 
dazu. Wenn ich so im unrecht bin, warum sind die Programme für den AVR 
so riesig? Warum arbeiten sie mit Bereichswerten und nicht mit starrem 
Schleifenzähler, meinetwegen auch festen Timercounts? Ich habe technisch 
nicht die Möglichkeit zu prüfen, was denn nun zu den Unterschieden 
führt. Ich denke aber mal, wenn ein und derselbe RC5 Empfänger an der 
C-Control feste Zeiten pro Bit hat und beim AVR nicht, wenn die Zeiten 
bei der DCF77 Uhr dermaßen schwanken, dann liegt für mich die Ursache 
eindeutig beim AVR. Wenn man dies berücksichtigt, dann kann man denke 
ich mit dem AVR alles machen. Die C-Control hat bei weitem nicht die 
Leistungsfähigkeit des AVR. In Assembler geschriebene Programme dürfen 
nur einige wenige Byte groß werden. Der Basic Interpreter ist extrem 
fehlerhaft, und, und, und.
lg
Peter

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Die unterschiedlich lange Schleifendauer ist nur durch 
dazwischenkommende Interrupts erklärbar.

Die Timer liefern auch genaue Werte, allerdings kann bei manuellem 
Reload Jitter auftreten, weswegen man bei hohen Frequenzen den 
Compare-Interrupt von Timer 1 verwenden sollte. Jitter gibt es zwar auch 
beim Aufruf dieses Interrupts, aber da der Timerwert hier nicht von Hand 
geändert werden muss, sondern automatisch zurückgesetzt wird, addiert 
sich dieser Fehler nicht.

Ich kann deine Probleme nicht nachvollziehen. Das DCF77-Programm kann 
ich leider nicht ausprobieren, da ich kein DCF77-Modul habe (bzw. meinen 
Wecker nicht zerlegen will).

Gruß
Andreas

von Peter Kutsch (Gast)


Lesenswert?

Hallo Andreas
Leider war nur der Int0 für die RC5 aktiv. Somit schwer erklärbar. Bei 
der DCF77 war nur INT 1 aktiv und an dem hing die DCF77. Wie gesagt, mir 
fehlen die technischen Möglichkeiten, um abzuklären, was die 
unterschiedlichen Timercounts/Zeitschleifen erklärt. Ich denke, ich habe 
sogar Ints abgeklemmt, gepollt und trotzdem keine korrekte Erkennung der 
RC5 mit Zeitschleifen hinbekommen. Aber egal, die AVR's sind super und 
ich bleibe dabei. Probleme sind keine mehr, wenn man sie als Problem 
erkannt hat und einen Ausweg dafür gefunden hat. Dies ist sowohl bei RC5 
als auch mit DCF77 gelungen. Man verwendet eben Zeitbereiche. Auch wenn 
das vielleicht für einen Perfektionisten nicht optimal erscheint, mir 
reicht es, wenn es funktioniert. Ich muß nicht für alles eine hieb und 
stichfeste Erklärung haben. Wenn mir einer eine gibt, super, wenn nicht, 
na dann eben klapt oder klapt nicht. Wenns klapt, bin ich zufrieden.
lg
Peter

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ich hab jetzt mal mein C-Programm in AVR Assembler umgesetzt.
Ich kann nicht behaupten, daß es mit 30 Zeilen (=60 Bytes) sehr lang 
ist.
Wie gesagt, alle Werte aus der Analogwelt können schwanken. Deshalb wird 
für ein Bit ein Zeitfenster von 0,75 ... 1,5 Bitzeiten getestet.

Ich habs ausprobiert, es läuft sowohl mit einem 8MHz als auch mit einem 
12MHz Quarz einwandfrei. Aber man sollte trotzdem die richtige 
Quarzfrequenz (z.B. 10MHz) eintragen.

Dein Assemblerprogramm habe ich nicht verwendet, da Du ja die gesamte 
Zeit im Interrupt verwartest. Und das sollte man besser vermeiden, 
schließlich soll die CPU ja noch anderes zu tun.


Peter

von Peter Kutsch (Gast)


Lesenswert?

Hallo Peter
damit wären wir ja mal wieder einig. Ein Zeitfenster muß her. Feste 
Werte klappen nicht. Mit Routinen, die beim AVR Zeitfenster verwenden, 
kannst Du die Strasse pflastern. Es ist mir aber kein Programm bekannt, 
welches mit festen Werten (ob über Schleifen oder über Timercounter) 
realisiert wurde.
lg
Peter

von Peter D. (peda)


Lesenswert?

Das liegt aber, wie gesagt, am analogen Charakter der 
Übertragungsstrecke und daran, daß in den Fernbedienungen billige 
unpräzise Schwinger verwendet werden.

Wenn Du Dir das C-Control-Programm genauer ansiehst, wartet die Funktion 
getbit auch innerhalb eines Zeitfensters auf einen Pegelwechsel und 
synchronisiert sich darauf neu. Und dieses Zeitfenster beginnt, genau 
wie bei mir, bei 3/4 * Bitzeit. Nichts ist es mit festen Werten.
Wenn das in Deinem AVR-Programm nicht so ist, dann kann es auch nicht 
funktionieren.


Wenn die Timer im AVR nicht auf den Quarztakt genau wären, dann müßte ja 
meine Digitaluhr ohne DCF77 nach dem Mond gehen und mein Frequenzmesser 
müßte auch ständig falsche Werte anzeigen, tun sie aber nicht.

Der Frequenzmesser ist 5-stellig und auf alle Stellen genau.
Und bei der Uhr sind erst nach einem Monat ein paar Sekunden Abweichung 
feststellbar.

Der Mikrokontroller ist nun mal kein Mensch und braucht deshalb für die 
selben Befehle immer genau die gleiche Zeit.


Peter

von Peter Kutsch (Gast)


Lesenswert?

Och Peter
guck mal richtig hin.
>>;--------------------------------------------------------------------- --------
>>; Warteroutine fuer 1/4 eine Biphasenbits = 444us = 444T bei 12Mhz
>>;                       ;Aufrufcall  2T
>>w444    lda #147     ; 3
>>loop    deca          ; 3  A -1
>>        bne loop     ; 3  6*0,5µs = 3µs/Schleife
>>  rts
Ich übersetze es Dir aber gerne.
w444 = Label
lda #147 = lade Akkumulator decimal 147
loop = label
deca = akkumulator --
bne loop = wenn akkumulator > 0 dann springe zum Label loop
rts = return subroutine

Wo ist das Zeitfenster?
Hier nochmal das Prog der C-Control


; RC5-Unterprogramm   RC5.ASm
; nach einer Vorlage von Olaf Kaluza

PC  .equ $01
Cnt .equ $0A3
Kon .equ $0A4
Adr .equ $0A5
Tas .equ $0A6

        .org $101
        Clr Kon
        Clr Adr
        Clr Tas
        brset 0,PC,rc6
rc5     brset 0,PC,rc5  ;Warten auf Startbit
        jsr w444        ;warten
        brset 0,PC,rc5  ;Stoerimpuls
        jsr w444  ;Naechsten Biphasenpegel
        jsr w444
        brclr 0,PC,rc5  ;Stoerimpuls
        jsr w444
        jsr w444
        brset 0,PC,rc5  ;Stoerimpuls
        jsr w444        ;Ok weiter
        jsr w444        ;Kontrollbit einlesen
        jsr getbit      ;Kontrollbit hier im Carry
        rol Kon
        ldx #5        ;5 Adressbits
rcadr   jsr w444
        jsr w444
        jsr getbit
   rol Adr    ;Bit merken
  dex
        bne rcadr
        ldx #6       ;6 Datenbits
rcdat   jsr w444
        jsr w444
        jsr getbit
        rol Tas         ;merken
        dex
        bne rcdat
rc6     rts                     ;Zurueck zum Hauptprogramm
;----------------------------------------------------------------------- 
------
;Einlesen eines Bits, Rueckgabe in Carry
getbit  lda  #255 ;Timeout festlegen
        sta  Cnt
        brset 0,PC,gethi  ;Wird es ein Lowbit?
gbl1    brset 0,PC,gbl2   ;Auf Biphasensprung syncronisieren
        dec Cnt
        bne gbl1
        bra biterr        ;Timeout
gbl2    jsr w444
        brclr 0,PC,biterr  ;Fehler
        clc                ;Lowbit
        rts
gethi   brclr 0,PC,gethi2   ;auf Phasensprung syncronisieren
        dec Cnt
        bne gethi
        bra biterr          ;Timeout
gethi2   jsr w444
        brset 0,PC,biterr ;Fehler?
        sec                ;Highbit
        rts
biterr  Clr Kon       ;Fehler, alles löschen
  clr Adr
   clr Tas
        clr Cnt
  rts

;----------------------------------------------------------------------- 
------
; Warteroutine fuer 1/4 eine Biphasenbits = 444us = 444T bei 12Mhz
;                       ;Aufrufcall  2T
w444    lda #147     ; 3
loop    deca          ; 3  A -1
        bne loop     ; 3  6*0,5µs = 3µs/Schleife
  rts

         .END
Mann, wenn Du meinst, in der Proc getbit wird ein Zeitfenster abgefragt, 
i9rrst Du. Es wird lediglich abgefragt, ob eine Störung auf der Leitung 
war. Wie gesagt, nicht reden machen. Bitte mach das doch bitte im AVR 
eins zu eins. Sind doch nur ein paar Befehle. Solltest Du Fragen zu 
einem Befehl haben, ich sage Dir gerne was welcher Befehl macht.
lg Peter

von Peter D. (peda)


Lesenswert?

Also vor getbit wird 3 * w444 aufgerufen, damit ist erst 3/4 der Zeit 
rum. Richtig ?

Dann in getbit steht:

getbit lda #255 ;Timeout festlegen

was soviel heißen soll, wie oberes Zeitlimit.

Dann kommt:

gethi brclr 0,PC,gethi2 ;auf Phasensprung syncronisieren

was soviel heißen soll, wie auf die Flanke synchronisieren.



Und darin wird wohl auch Dein Fehler liegen. Wenn, wie Du sagst, der AVR 
100-mal schneller ist, müßte Dein Timeoutzähler dann ja auf 25500 
gesetzt werden, d.h. ein Byte reich da nicht mehr.


Peter

von Peter Kutsch (Gast)


Lesenswert?

Hallo Peter
wo liegt das Problem? Machs doch genau so im AVR. Und damit soll es nun 
auch genug sein. Ich weis nicht, vielleicht drücke ich mich 
unverständlich aus. Mach  es  genau  so  wie  bei  der C-Control.  Wenn 
es  dann  funktioniert,  dann  gebe  ich  mich geschlagen.
lg
Peter

von Peter D. (peda)


Lesenswert?

Ich will Dich nicht schlagen.

Ich machs doch im Prinzip wie bei der C-Control.

Ob nun Timerinterrupt oder Delay-Loop ändert doch am Prinzip überhaupt 
nichts.

Es macht nur die Implementierung leichter.
Statt:
.equ XTAL = 10000000 ;10Mhz
kann ich auch schreiben:
.equ XTAL = 4000000 ;4MHz
und es funktioniert auch sofort mit dem anderen Quarz.

Versuch mal, das C-Control-Programm nachzuvollziehen. Dann wirst Du 
sehen, daß es genauso arbeitet, wie meine Routine.


Peter

von Peter Kutsch (Gast)


Lesenswert?

Lieber Peter
ich habe das C-Control Programm erst exakt, dann in ich weis nicht wie 
vielen Variationen umgesetzt. Es klapt eben nicht. Die Diskusion geht 
langsam in eine Richtung, die keinen Erfolg verspricht.
Also, wenn du eine exakte Umsetzung des Programmes für den AVR hast, und 
nicht etwas mit Zeitfenstern oder ähnliches, dann bin ich der Erste, der 
zugibt, dass ich im Unrecht bin.
Solange ich das nicht sehe, sage ich:
1. Delays über Zeitschleifen mit festem Schleifenzähler kannst Du 
vergessen. Dies wird auch unisono in den diversen Foren bestätigt. Lies 
Dir vielleicht noch mal die Beiträge durch.
2. Auch mit dem Timercounter ist Vorsicht geboten. Auch dies wird in 
anderen Foren bestätigt.
3. Ich sage nicht, woran das liegt, denn das weis ich nicht. Es gibt 
eine Reihe von Möglichkeiten woran es liegen könnte. Auch diese sind in 
den Beiträgen zu Warte-Befehl in Assembler aufgeführt.
lg Peter

von Peter D. (peda)


Lesenswert?

Daß Dein AVR-Programm gar nicht gehen kann, weil der Timeout-Zähler für 
die obere Zeitschwelle viel zu klein ist, hast Du wohl überlesen. 
(Letzter Satz im 4.Beitrag über diesem).

Das ist diese Zeile in Deinem Programm:

ser r24 ;Cnt=255

Nötig wäre >700, d.h. 2 Register.


Peter

von mikki merten (Gast)


Lesenswert?

Langsam wird die Diskussion hier etwas diffus. Am Ende weiss wohl keiner 
mehr wie die Timer und Interrupts bei der AVR Familie wirklich 
funtionieren. Ich möchte
an dieser Stelle nur folgendes anmerken sowohl mit
reinen Zählschleifen als auch mit Timer-gesteuerten
Routine sind auf 1clk genaue Delays möglich es kommt
immer auf die Programmstruktur an.

von Andreas S. (andreas) (Admin) Benutzerseite


Angehängte Dateien:

Lesenswert?

@Peter:

1. Zu den Zeitschleifen:
Mach das Programm aus dem Anhang in einen AVR. Schließ ein Oszi an PB0 
an. Freu dich über das wunderhübsche, jitterfreie Rechteck.

2. Zu den Timern:
Toggel ein Pin im Compare-Interrupt von Timer 1, schreib irgendwas 
sinnloses kompliziertes in die Hauptschleife. Freu dich über das 
wunderhübsche, mit vorhersagbarem Jitter versehene Rechteck.

Mehr gibt es zu dem Thema nicht mehr zu sagen.

von mikki merten (Gast)


Lesenswert?

Wenn dem so wäre, dass ich beim Timer gesteuerten Delay immer mit einem 
Jitter leben müßte, hätte ich
den Einsatz Einsatz von AVR Prozessoren längst be-
endet bzw. hätte die Prozessorarchitektur nicht richtig verstanden.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Du mußt mit dem Jitter leben. Er entsteht dadurch, dass beim Auftreten 
des Interrupts der aktuelle Befehl noch fertig abgearbeitet wird. Die 
Zeitunterschiede sind natürlich minimal, aber bei sehr hohen 
Interruptfrequenzen durchaus spürbar. Wenn du den Timer 0 ohne Prescaler 
und mit manuellem Reload verwendest, setzt sich der Fehler sogar fort. 
Das hat mich schonmal mehrere Stunden Fehlersuche gekostet...

von mikki merten (Gast)


Lesenswert?

Also ich nicht. Der Unterschied zwischen den Aus-
führungszeiten 1/2 Takte ist ja sehr gering und in
einer Interruptroutine sehr leicht ausgleichbar.
Ohne Prescaler ist auch das manuelle Reload ohne Jitter möglich. Da ich 
ja immer eine genaue Kontrolle
über jeden einzelnen Taktimpuls habe. Etwas komplexer
werden die Korrekturen bei eingeschaltetem Prescaler.
Bei den neueren AVRs der MEGA-Familie hat Atmel zusätzlich die 
Möglichkeit eines Prescaler Reset spen-
diert, welchses die Programmierung an dieser Stelle
erheblich vereinfacht.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

In meinem Programm (DDS) wird der Interrupt alle 24 Takte ausgelöst, da 
fallen ein oder zwei Takte Unterschied schon ins Gewicht.

Mit Prescaler hast du doch ewig Zeit den Timerwert zu setzen, ohne 
Prescaler läuft der Wert schon weiter während du ihn ausliest. Wenn man 
anstatt einen festen Wert zu laden den Zählerstand mit subi ändert, 
sollte sich der Jitter theoretisch trotzdem eliminieren lassen. Ich hab 
das damals erst so probiert, es gab jedoch irgendwelche Probleme die 
mich zum Umstieg auf Timer 1 gezwungen haben. Müsste das Ganze 
vielleicht irgendwann nochmal simulieren und vielleicht einen "Artikel" 
dazu schreiben...

von mikki merten (Gast)


Lesenswert?

Ohne Prescaler und ohne weitere aktive Interrupts sorgt dieser 
Kunstgriff für einen Ausgleich der 2
unterschiedlich Befehlslängen. Allerdings sind die
Möglichkeiten wie zusätzliche WAIT-States bei externem Speicher (ist mir 
sowie zu langsam und maximal 4K
internes SRAM dürften wohl in jedem Fall reichen) nicht berücksichtigt.

in  R16,TCNTx(L)   ;R16 = aktueller Wert Timer 0,1,2
sbrc R16,0         ;Bit0 = 0 -> jump = 2 Takte sonst
                   ;no JUMP = 1 Takt
sbrs R16,0         ;immer 2 Takte


Bei aktivem Prescaler ist die Sache mit der taktge-
nauen Synchronisation etwas aufwendiger, da ja hier
keinerlei Möglichkeit besteht, den Zählerstand des
Prescalers auszulesen.

von Uwe (Gast)


Lesenswert?

Hi!
gerade mit Prescalers geht es am besten. bei zb.einem Prescalers von 
1024 hast du genau 1024 Takte Zeit um dich um den Timerwert zu kümmern 
ohne das Verfälschungen auftreten.
Gruss Uwe

von mikki merten (Gast)


Lesenswert?

Wenn der Wert des Timer-Register TCNTx von Bedeutung ist, hast du recht. 
Wenn du aber z.B. ein Rechteck mit exaktem 50/50 Tastverhältnis an einem 
Port via Timer-Interrupt erzeugen willst und z.B. noch Interrupts vom 
UART und SPI autreten, wird's bei aktivem Prescaler richtig schwierig 
dieses zu realisieren, da man ja mit unterschiedlichen Response-Zeiten 
beim Timer-Interrupt rechnen muß.

von Stefan (Gast)


Lesenswert?

Hallo,

wow was für thread !
ich fand es hochspannend die Beiträge zu verfolgen

eine Frage:

könnte von euch jemand die DCF-Decodierroutine
so in assembler schreiben das man sie in ein
Bascom-Basicprogramm einbinden kann?

Es gibt allerding einige Dinge die man dabei beachten
muß

Die Hilfebeschreibung dazu weiter unten

eine andere Möglichkeit wäre das Assemblerprogramm
einmal ausführlich zu kommentieren so das man es auch
als Basic/Turbo-Pascal-Programmierer versteht.

Dann könnte ich etwas äquivalentes in Basic programmieren oder wäre der 
Basic-code nicht schnell
genug?

Vielen Dank schon im Voraus

viele herzliche Grüße

Stefan


Mixing ASM and BASIC

BASCOM allows you to mix BASIC with assembly.
This can be very useful in some situations when you need full control of 
the generated code.

Almost all assembly mnemonics are recognized by the compiler. The 
exceptions are : SUB, SWAP,CALL and OUT. These are BASIC reserved words 
and have priority over the ASM mnemonics. To use these mnemonics precede 
them with the ! - sign.
For example :

Dim a As Byte At &H60  'A is stored at location &H60
Ldi R27 , $00      'Load R27 with MSB of address

Ldi R26 , $60      'Load R26 with LSB of address
Ld R1, X      'load memory location $60 into R1
!SWAP R1      'swap nibbles

As you can see the SWAP mnemonic is preceded by a ! sign.

Another option is to use the assembler block directives:
$ASM
  Ldi R27 , $00      'Load R27 with MSB of address
  Ldi R26 , $60      'Load R26 with LSB of address
  Ld R1, X        'load memory location $60 into R1
  SWAP R1      'swap nibbles
$END ASM


A special assembler helper function is provided to load the address into 
the register X or Z. Y can may not be used because it is used as the 
soft stack pointer.


Dim A As Byte    'reserve space
LOADADR a, X    'load address of variable named A into register pair X

This has the same effect as :
Ldi R26 , $60  'for example !
Ldi R27, $00   'for example !

Some registers are used by BASCOM
R4 and R5 are used to point to the stack frame or the temp data storage
R6 is used to store some bit variables:
  R6 bit 0 = flag for integer/word conversion
  R6 bit 1 = temp bit space used for swapping bits
  R6 bit 2 = error bit (ERR variable)

  R6 bit 3 = show/noshow flag when using INPUT statement
R8 and R9 are used as a data pointer for the READ statement.

All other registers are used depending on the used statements.

To Load the address of a variable you must enclose them in brackets.
Dim B As Bit

Lds R16, {B}  'will replace {B} with the address of variable B

To refer to the bitnumber you must precede the variable name by BIT.
Sbrs R16 , BIT.B   'notice the point!
Since this was the first dimensioned bit the bit number is 7. Bits are 
stored in bytes and the first dimensioned bit goes in the LS bit.

To load an address of a label you must use :
LDI ZL,  Low(lbl * 1)
LDI ZH , High(lbl * 1)
Where ZL = R30 and may be R24, R26, R28 or R30
And ZH = R31 and may be R25, R27, R29 or R31.

These are so called register pairs that form a pointer.

When you want to use the LPM instruction to retrieve data you must 
multiply the address with 2 since the AVR object code consist of words.
LDI ZL,  Low(lbl * 2)
LDI ZH , High(lbl * 2)
LPM ; get data into R0
Lbl:

Atmel mnemonics must be used to program in assembly.
You can download the pdf from www.atmel.com that shows how the different 
mnemonics are used.
Some points of attention :
* All instructions that use a constant as a parameter only work on the 
upper 16 registers (r16-r31)

So LDI R15,12  WILL NOT WORK

* The instruction SBR register, K
will work with K from 0-255. So you can set multiple bits!

The instruction SBI port, K  will work with K from 0-7 and will set only 
ONE bit in a IO-port register.

The same applies to the CBR and CBI instructions.

You can use constants too:
             .equ myval = (10+2)/4
              ldi r24,myval+2    '5
              ldi r24,asc("A")+1  ; load with 66

Or in BASIC with CONST :

CONST   Myval  = (10+2) / 4

Ldi r24,myval

How to make your own libraries and call them from BASIC?
The files for this sample can be found as libdemo.bas in the SAMPLES dir 
and as mylib.lib  in the LIB dir.

First determine the used parameters and their type.
Also consider if they are passed by reference or by value

For example the sub test has two parameters:
 x which is passed by value (copy of the variable)
 y which is passed by reference(address of the variable)


In both cases the address of the variable is put on the soft stack which 
is
 indexed by the Y pointer.

The first parameter (or a copy) is put on the soft stack first
To refer to the address you must use:
    ldd r26 , y + 0
    ldd r27 , y + 1
This loads the address into pointer X


The second parameter will also be put on the soft stack so :
The reference for the x variable will be changed :

To refer to the address of x you must use:
   ldd r26 , y + 2

   ldd r27 , y + 3


To refer to the last parameter y you must use
   ldd r26 , y + 0
   ldd r27 , y + 1


Write the sub routine as you are used too but include the name within 
brackets []

[test]
test:
  ldd r26,y+2   ; load address of x
  ldd r27,y+3
  ld r24,x         ; get value into r24
  inc r24          ; value + 1
  st x,r24         ; put back
  ldd r26,y+0  ; address of y
  ldd r27,y+1
  st x,r24         ; store
  ret                 ; ready

[end]


To write a function goes the same way.
A function returns a result so a function has one additional parameter.
It is generated automatic and it has the name of the function.
This way you can assign the result to the function name
For example:

Declare Function Test(byval x as byte , y as byte) as byte
A virtual variable will be created with the name of the function in this 
case test.
It will be pushed on the softstack with the Y-pointer.

To reference to the result or name of the function (test) the address 
will be:
   y + 0 and y + 1
The first variable x will bring that to y + 2 and y + 3
And the third variable will cause that 3 parameters are saved on the 
soft stack
To reference to test you must use :
   ldd r26 , y + 4
   ldd r27 , y + 5

To reference to x
   ldd r26 , y + 2
   ldd r27 , y + 3
And to reference y
  ldd r26 , y + 0
  ldd r27 , y + 1


When you use exit sub or exit function you also need to provide an 
additional label.  It starts with sub_ and must be completed with the 
function / sub routine name.  In our example:

sub_test:


When you use local variables thing become more complicated.
Each local variable address will be put on the soft stack too
When you use 1 local variable its address will become
   ldd r26,  y+0
   ldd r27 , y + 1
All other parameters must be increased with 2 so the reference to y 
variable changes  from
  ldd r26 , y + 0 to ldd r26 , y + 2
  ldd r27 , y + 1 to ldd r27 , y + 3
And of course also for the other variables.

When you have more local variables just add 2 for each.

Finally you save the file as a .lib file
Use the library manager to compile it into the lbx format.
The declare sub / function must be in the program where you use the sub 
/ function.

The following is a copy of the libdemo.bas file :

'define the used library
$lib "mylib.lib"

'also define the used routines
$external Test

'this is needed so the parameters will be placed correct on the stack
Declare Sub Test(byval X As Byte , Y As Byte)


'reserve some space

Dim Z As Byte


'call our own sub routine
Call Test(1 , Z)

'z will be 2 in the used example
End

When you use ports in your library you must use .equ to specify the 
address:
.equ EEDR=$1d
In R24, EEDR

This way the library manager know the address of the port during compile 
time.

As an alternative precede the mnemonic with a * so the code will  not be 
compiled into the lib. The address of the register will be resolved at 
un time in that case.


This chapter is not intended to learn you ASM programming. But when you 
find a topic is missing to interface BASCOM with ASM send me an email.

von Peter D. (peda)


Lesenswert?

Hallo Stefan,

mal als Tip:

Die Diskussion wird dadurch nicht gerade gut lesbar, wenn man riesige 
Textbandwürmer mittendrin einfügt.

Besser ist es, dafür die Option "Dateianhang" anzuklicken.


Peter

von mikki merten (Gast)


Lesenswert?

Dem kann ich nur zustimmen. Zumal es sich hier um Auszüge aus einem 
Handbuch handelt, würde ein entsprechender Link ausreichen. Auf der 
BASCOM Hompage
findet sich übrigens auch ein entsprechendes DEMO-Programm zur DCF77 
Decodierung in BASIC

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.