Hallo,
da ich mir gerade Assembler beibringen möchte und mein Wissen Anhand von
Beispielaufgaben überprüfen möchte, habe ich eine Frage an euch:
An Port 1.0 will ich eine periodische Flanke im Abstand von 1,6ms
erzeugen, wobei die Taktfrequenz 24MHz beträgt. Der Timer0 soll im Modus
2 (8Bit Autoreload) verwendet werden.
Daher erst mal meine Berechnung zu den Zyklen::
24MHz / 12 = 2 MHz = 5*10^-7 s
1,6ms / 5*10^-7s = 3200 benötigte Maschinenzylken
Das heißt, ich brauche eine Schleife, da mein 8 Bit Timer höchstens den
Wert 255 speichern kann. 3200 / 200 = 16 Schleifendurchgänge
Dann Initialisiere ich den Timer, R0 und setze die Interruptfreigaben:
siebert schrieb:> cjne R0, #16,weiter ; Wenn nicht 200 * 16 = 3200 Schritte vergangen> sind> CPL P1.0 ; Bit 0 von Port 1 invertieren, um Signalflanke zu erzeugen
und hier muss ich natürlich noch: mov R0,#0 ergänzen, um meine
Zählvariable zu resetten
Mario M. schrieb im Beitrag #5543649:
> Der Interrupt will noch "enabled" werden.
SETB EAL ; Intterupts Freigeben
SETB IT0 ; Timer 0 Interrupt freigeben
Ist er hiermit nicht "enabled"? Wie mache ich dies, falls das nicht der
Fall ist?
Mario M. schrieb im Beitrag #5543649:
> Außerdem ist push und pop> falsch geschachtelt.
Wie sonst ?
MfG
siebert schrieb:> Mario M. schrieb im Beitrag #5543649:>> Außerdem ist push und pop>> falsch geschachtelt.>> Wie sonst ?
Was man zuletzt gepusht hat muss man zuerst poppen.
Georg
georg schrieb:> Was man zuletzt gepusht hat muss man zuerst poppen.
Achso, das wusste ich bisher noch gar nicht, klingt aber logisch, vielen
Dank! Hast du sonst noch Vorschläge oder sieht der Rest in Ordnung aus?
hier lang schrieb:> Schreib das Ganze in C und erzeuge ein ASM file ohne Optimierung. Damit> fängst du an zu verstehen.
Das hört sich so an als wäre mein Code falsch, aber was genau?
Klaus R. schrieb:
>...wieso bitte ASM auf einem 8051? Baust du dir morgen auch ein>Röhrenradio? Installierst du noch DOS und dann Win3.11?
Wenn es ihm Spaß macht, warum nicht? Mir macht auch
Röhrenradios bauen noch Spaß, ich besitze auch noch
ein 286-Computer mit MS-DOS. Ich habe aber auch schon
Radios mit Transistoren und ICs als Doppelsuper gebaut.
Hallo Siebert,
entschuldige, dass ich mich hier einmische. Ich kann eigentlich gar
nicht den 8051 programmieren, nur MSP430 und Z8. Macht nix, mir ist
folgendes aufgefallen:
1.
Früher wurde die Verwendung des Stack wie folgt erklärt. Der Stack ist
wie der Stapel Teller in der Mensa zu betrachten. Legt man einen Teller
auf den Stapel, wird dieser als erster Teller wieder genommen, wenn man
sich einen Teller vom Stapel nimmt. Genauso funktioniert das, wenn man
einen Satz von mehreren Tellern auf den Stapel legt. In der Reihenfolge
werden sie wieder entnommen. Man kann nichts aus dem Stapel
herausziehen, da die Teller im Schacht sind.
2. An einer Stelle zur Initialisierung setzt Du:
mov R0,#0
und dann später:
inc R0
cjne R0, #16, weiter
Schön und gut. Du vergleichst in der letzten Zeile Dein Register mit 16
und springst, wenn nicht gleich zu "weiter" weiter - ja, weiterspringen
zu weiter.
Versuche andere Bezeichnungen für Label (Sprungadressen) zu verwenden.
Viele Compiler verwenden bei der Compilation den Namen der Funktion, in
der man sich gerade befindet und setzen einen Tiefstrich und eine Zahl
dahinter. Z. B. für die Prozedur/Funktion "init" könntest Du als
Sprungnamen "init_9" verwenden. Immer wenn du "..._9" in Deinem Code
siehst, weist Du, Du springst zum Ende. Du kannst aber einfach auch
Deine Sprungadressen hochzählen. - Das war Kosmetik.
Jetzt kommen wir zum Vergleich. "Sauberer" programmiert man, wenn man
den Wertebereich Deiner Variablen eingrenzt. Du zählst nur von 1 bis 16
oder von 0 bis 15. Geschickt ist es, wenn Du rückwärts zählst und bei
erreichen einer Null dann verzweigst. Also ich verwende immer so etwas:
decrement jump not zero (djnz - beim Z8)
oder
decrement jump zero (djz- auch beim Z8)
Du setzt Deine Variable auf den Ini-Wert 16 und zählst rückwärts.
Du kannst auch, gerade in Assembler sehr leicht, Deine Variable
maskieren, mit (0x0f) - Hex-Darstellung. Damit stellst Du sicher, dass
Deine Variabel niemals unerlaubte Werte annimmt.
Stell Dir einmal vor, Dein R0 erhält durch Zufall eine 17, dann
inkrementierst Du durch den gesamten Zahlenraum, bis R0 wieder 16 ist.
... und das kann dauern. Hierzu werden von anderen Foristen massenhaft
Kommentare kommen. Mein Beispiel ist aber auch wieder Geschmackssache.
Wenn Du meinst, Du brauchst auch die 16 (Dezimal), dann sei Dir erlaubt,
mit 0x1f (Hexadezimal 31) zu maskieren. Dass ein Prozessor durch ein
kosmisches oder komisches Teilchen außer Tritt gerät kann vorkommen.
Manchmal überschreibt der Programmierer auch selbst seine Register an
anderer Stelle.
Auf jeden Fall beglückwünsche ich Dich in Deiner Entscheidung mit
Assembler-Code anzufangen! Der Vorschlag ohne Schnörkel und einfach in C
Code zu schreiben hat auch etwas für sich: Wie beschrieben kann man sich
das Assembler-Listing des Compilers (das ist ein Zwischenfile, für das
man in der Regel etwas in der Entwicklungsumgebung anklicken muss)
ansehen. Mit diesen Listing kann man viele kleine Tricks lernen.
Ach ja, ich hatte früher auch einmal ein Kalenderprogramm zu schreiben.
Dabei hatte ich den Variablennamen "tag" für den Tag verwendet. Das
mochte der Compiler garnicht und hat mich fehlersuchenlassen - damals.
Happy coding!
Bernd
Hallo Günther,
aber als Einstieg ist das doch etwas ungewöhnlich...Arduino ist zu weit
weg, ASM zu tief drin. Man sollte wissen, dass es ASM gibt und sich
damit dann mal beschäftigen (ggf inline), die ersten Schritte würde ich
aber in C machen. Ein 8 Jähriger fämgt ja auch nicht mit einem 286er an
und stellt IRQs & DMAs ein, nur weil man das "früher so gemacht hat".
Aber Meinungen dürfen unterschiedlich sein.
Klaus.
Hallo Bernd,
vielen Dank für deine ausführliche und hilfreiche Antwort.
Du hast natürlich recht, ein djnz Befehl statt dem incrementieren wäre
die schönere Lösung, aber das lässt sich ja schnell ändern.
Die Sprungmarken werde ich nach Möglichkeit auch umbennen, vielen Dank
dafür!
Für die anderen:
Es ist doch egal, ob es sinnvoll ist oder nicht. C# habe ich schon
gelernt und im nächsten Semester habe ich ein Modul, wo ich yC mit
Assembler programmieren muss, unter anderem auch den 8051 und PIC16.
Ich übe in den Semesterferien jetzt schon mal etwas, sodass ich im
Semester mehr Zeit für die anderen Module habe.
Meine Frage ist nur, ob das Programm vom Prinzip so funktioniert oder ob
ich die ISR umschreiben muss, ob die Initialisierung des Timers etc.
korrekt ist.
siebert schrieb:> Die Sprungmarken werde ich nach Möglichkeit auch umbennen,> vielen Dank dafür!
Die Sprungmarken mit dem Funktionsnamen zu benennen, ist eine gute Idee.
Besser als "einfach hochzählen" wäre aber eine inhaltlich sinnvolle
Benennung für den Rest. :-)
siebert schrieb:> Das heißt, ich brauche eine Schleife, da mein 8 Bit Timer höchstens den> Wert 255 speichern kann.
Hat dein Timer einen Prescaler?
Falls ja, kannst du auf die Schleife verzichten.
siebert schrieb:> Es ist doch egal, ob es sinnvoll ist oder nicht. C# habe ich schon> gelernt und im nächsten Semester habe ich ein Modul, wo ich yC mit> Assembler programmieren muss, unter anderem auch den 8051 und PIC16.
Nenne es besser µC, sonst zeigst du nur, dass du Anfänger bist. :-)
Die Grundzüge von Prozessoren kann man auch auf einem 8051 lernen, auch
wenn man das heutzutage nicht mehr unbedingt möchte. Für die Uni muss
man da aber auch nicht viel mehr können, insofern: Viel Erfolg!
Wenn du mit C anfangen möchtest, sind andere Architekturen (z.B. AVR
oder ARM) allerdings besser geeignet als dein 8051. Wobei es damit auch
irgendwie geht.
Klaus R. schrieb:> ...wieso bitte ASM auf einem 8051? Baust du dir morgen auch ein> Röhrenradio?
Das ist einfach nur ignorante Unkenntnis. Es gibt noch hunderte aktuelle
8051-Derivate, auch wenn nicht mehr alle das 8051 im Namen haben. Aber
wenn man nichts kennt ausser Arduino...
Georg
Wer Assembler nicht versteht, versteht auch nicht warum sein C-Programm
nicht richtig läuft.
Gerade im Embedded Bereich ist eine Vermischung von C und Assembler ganz
normal. Und es ist immer hilfreich zu verstehen, was der Compiler aus
meinem Code bastelt.
Insofern halte ich die Kenntnis von Assembler für sehr wichtig.
Peter D. schrieb:> Der Kommentar ist falsch. IT0 setzt den externen Interrupt INT0 auf> Flankentriggerung. Lade Dir erstmal das Datenblatt des jeweiligen 8051> runter.
Da hast du natürlich recht, danke!
Ich meinte setb ET0, ( ET0 gibt Interrupts vom Überlauf des
Zeitgeber/Zählers 0 frei (ET0 = 1) oder nicht frei (ET0 = 0)).
Ich würde das Timerflag als erstes löschen. Es könnten ja weitere Ints
kommen während Du noch in der ISR bist, 1 Timer reicht nicht lange wenn
auch mit Variablen damit so einige Zeitschleifen realisierbar sind. Es
sei denn Du setzt als erstes DI, dann kanst Du in der ISR machen was Du
willst.
Stephan schrieb:> Ich würde das Timerflag als erstes löschen.
Das wird automatisch beim Eintritt gelöscht.
Stephan schrieb:> Es könnten ja weitere Ints> kommen während Du noch in der ISR bist
Das stört beim 8051 nicht, man kann ja bis zu 4 Prioritäten vergeben.
Der Timer unterbricht dann einfach längere Interrupts, damit kein Tick
verloren geht.
Klaus R. schrieb:> ...wieso bitte ASM auf einem 8051? Baust du dir morgen auch ein> Röhrenradio? Installierst du noch DOS und dann Win3.11?
Ja, warum nicht ?
> Lass es...
Du würdest es lassen, ja, aber du bist nicht der Maßstab der Welt.
> Klaus.
Wer sonst.
hier lang schrieb:> Schreib das Ganze in C und erzeuge ein ASM file ohne Optimierung.> Damit fängst du an zu verstehen.
Nicht wirklich. C Compiler für 8051 erzeugen gar merkwürdigen code.
Er sollte sich lieber Beispiele fremder Leute angucken von
timerbasierten Programmen (und die zum Test mal laufen lassen).
Das Programm wird nicht funktionieren, jedenfalls nicht wie gewünscht.
Abgesehen von der falschen Reihenfolge der Stackbefehle, wurde ja schon
erwähnt, fehlt ein ganz entscheidendes Detail: nachdem r0 seinen
Prüfwert erreicht hat, muß r0 auf seinen Startwert zurückgesetzt werden.
Wenn das nicht geschieht, wird r0 weiter bis zum Überlauf erhöht und
erreicht erst danach den Prüfwert -> die Zykluszeit stimmt nicht mehr.
MaWin schrieb:> hier lang schrieb:> Schreib das Ganze in C und erzeuge ein ASM file ohne Optimierung. Damit> fängst du an zu verstehen.>> Nicht wirklich. C Compiler für 8051 erzeugen gar merkwürdigen code.>> Er sollte sich lieber Beispiele fremder Leute angucken von> timerbasierten Programmen (und die zum Test mal laufen lassen).
Nö, ohne Optimierung sieht das ganz normal aus. Wenn er wirklich ASM
verstehen will, ist das eine gute Methode.
G. O. schrieb:> muß r0 auf seinen Startwert zurückgesetzt werden.> Wenn das nicht geschieht, wird r0 weiter bis zum Überlauf erhöht und> erreicht erst danach den Prüfwert -> die Zykluszeit stimmt nicht mehr.
Radio Eriwan: Ja, aber wir wissen nicht, ob es Code gibt, wo R0 neu
gesetzt wird. Das vollständige Programm ist nicht veröffentlicht.
Gruß
Bernd
Hallo Siebert,
sofern Du noch mitliest, dürfte eines klar geworden sein: Ein Programm
strickt man nicht so aus dem Bauch heraus vor dem Bildschirm.
Damals, als Hannibal mit den Elefanten über die Alpen zog und ich meine
ersten Programme geschrieben habe, wurde uns in der Uni immer gesagt,
zum Programmieren benötigt man Papier und einen Bleistift. Wenn der
Ablauf klar ist, kann man ihn in Code gießen.
Ich kann Dir leider kein gutes Buch empfehlen, um Programmieren zu
lernen. Die Bücher heutzutage sind alle dermaßen überladen, dass die
Basics viel zu kurz kommen für die Newbies. Daher ist die Idee sich den
Code bei Kollegen anzusehen sicher nicht verkehrt. Oder man liest die
Beispielprogramme der Prozessorhersteller. (Hier wieder, die Beispiele
zum MSP430 waren und sind exzellent aufbereitet. Aber ich kenne ja nur
diese und somit weiß ich gar nicht, dass die anderen Firmen oder
Compiler-Hersteller viel bessere Produkte erzeugen.) Auch ist (war) es
gute Praxis den eigenen Code einem Kollegen zu erklären, der einen dann
auf "Macken" hingewiesen hat.
Zunächst hast Du ja einiges dazu gelernt: Namensvergebung, OP-Code
kennenlernen, Stackverwaltung.
Noch ein paar Empfehlungen: Interrupt-Routinen sollten so schnell wie
möglich verlassen werden !!! Sonst blockieren sie den Ablauf anderer
Interrupts. Daumenregel, mehr als eine halbe Bildschirmseite sollte eine
ISR in Assembler nicht lang sein. Man kann mit Flags arbeiten, die dann
von anderen Code-Abschnitten ausgewertet werden.
Unterprogramme kurz halten und modular aufbauen. Programme kann man
zunächst mit Struktogrammen auf dem Papier erstellen und kurz vor Ende
in Code schreiben.
Statt Loops im Endlos, vielleicht einmal den Prozessor im Low Power Mode
anhalten. (... kann der 8051 sicher auch)
Gültigkeitsbereiche von Loop-Variablen checken und eingrenzen.
Das kann man fortsetzen. Aber noch eine Sache: Du kannst auch mit einem
Timer mehrere Zeitabläufe realisieren, oder anders gesagt, Du kannst mit
N Timern mehr als N Zeitabläufe realisieren, in dem der / die Timer
endlos laufen und bei Compare-Werten Interrupts auslösen, die
entsprechen verarbeitet werden. Die Capture/Compare-Werte müssen dabei
laufend aktualisierst werden. Aber was ist eine Addition und ein
Register-Update in Assembler? - 2 Anweisungen und ein paar Bytes. (Das
kann der 8051 bestimmt auch.)
Viel Erfolg bei Deinen Projekten und
happy coding!
Bernd
PS: Die Beiträge der anderen Foristen finde ich hier einmal richtig gut!
Bernd B. schrieb:> Statt Loops im Endlos, vielleicht einmal den Prozessor im Low Power Mode> anhalten. (... kann der 8051 sicher auch)
Davon würde ich dringend abraten. Du reißt Dir damit völlig unnötig eine
zusätzliche Baustelle auf, die Du während der Entwicklung überhaupt
nicht brauchen kannst.
Das Stromsparen kommt erst hinzu, nachdem die eigentliche Aufgabe
einwandfrei läuft und nachdem man sich sicher ist, daß es überhaupt mit
ner Batterie lange laufen soll. Einem Netzteil ist es völlig schnurz, ob
es nun 5mA oder 5µA liefern muß.
Peter D. schrieb:> Davon würde ich dringend abraten.
Hallo Peda,
schon verstanden, aber sooo schwierig ist das nicht! Ich setze in meinen
Loops einfach eine Anweisung rein, den LPM zu initiieren. In den ISR
setze ich dann das entsprechende Flag vor der Rückkehr auf dem Stack und
der Prozessor läuft dann nach dem iret den Loop einfach weiter durch.
Wieder einmal nur zwei Anweisungen im Assembler und wenige Bytes Code.
Dafür hat man definierte Abläufe wenn man mit einem Oszi an den
Ausgang-Pins rumfummelt - kein Jitter - und wenn man sich in einem
minimalistischen Programm mit dem Vorgehen auseinander setzt und
versteht, hat man wieder etwas fürs Leben gelernt und kann das leicht in
den nächst größeren Projekten verwenden.
Gruß
Bernd
Bernd B. schrieb:> Radio Eriwan: Ja, aber wir wissen nicht, ob es Code gibt, wo R0 neu> gesetzt wird.
Es gibt nur eine erlaubte Position an der r0 zurückgesetzt werden darf,
nämlich genau nach der erfüllten Prüfbedingung und vor dem Label
"weiter:".
Jeder andere Rücksetzvorgang stört den Programmablauf.
Hallo Leute, danke für eure zahlreichen Antworten.
Zum Thema Variablen resetten, siehe dritter Beitrag:
> cjne R0, #16,weiter ; Wenn nicht 200 * 16 = 3200 Schritte vergangen> sind> CPL P1.0 ; Bit 0 von Port 1 invertieren, um Signalflanke zu erzeugen
und hier muss ich natürlich noch: mov R0,#0 ergänzen, um meine
Zählvariable zu resetten
jetzt habe ich das ganze mit cjne geschrieben und r0 wird
dementsprechend mit #16 wieder geladen
Bernd B. schrieb:> Wieder einmal nur zwei Anweisungen im Assembler und wenige Bytes Code.
Es geht nicht um die Codegröße, sodern um die höhere Komplexität und
damit Fehleranfälligkeit. In der Regel sind es verschiedene Instanzen,
die Stromsparen erlauben und beenden. Da kann man sehr schnell ins
Grübeln kommen, warum das Main nicht mehr das Gewünschte macht, weil es
schläft. Will man es sauber implementieren, braucht man eine
Statemaschine, die klar bestimmt, wann geschlafen werden darf und wann
nicht.
Was vielleicht bei nem simplen Testprogramm mit nur einem Interrupt und
leerer Mainloop noch überschaubar scheint, ist es bei größeren
Applikationen nicht mehr.
Der Rat, Schlafen erst nach Fertigstellung der Funktion und nur bei
Sinnhaftigkeit zu implementieren, entstand aus eigener Verzettelung,
wenn man alles gleichzeitig zu machen versucht.
Aufgaben zu unterteilen und klar zu trennen, erleichtert das
Programmieren erheblich.
georg schrieb:> Klaus R. schrieb:>> ...wieso bitte ASM auf einem 8051? Baust du dir morgen auch ein>> Röhrenradio?>> Das ist einfach nur ignorante Unkenntnis. Es gibt noch hunderte aktuelle> 8051-Derivate, auch wenn nicht mehr alle das 8051 im Namen haben. Aber> wenn man nichts kennt ausser Arduino...
Hi,
man muss schon auf eine Reihe von Annehmlichgkeiten verzichten... aber
es gibt gute Gründe, sich mit den Chips zu beschäftigen... zum Beispiel
zugunsten der Betriebssicherheit.
Das Programmieren hat bei mir keine Labels, so dass ich die Adressen zu
Fuß ausrechnen muss, ist bei Einbyte-, Zweibyte- etc. Befehlen gar nicht
so simpel.
Hat auch keinen Programmspeicher onboard, benötigt ein externes EPROM
fürs
Programm und noch ein Adressen-Latch-IC zusätzlich.
(Selbstbau-Programmiergerät ist vorsintflutlich.)
Trotzdem, oder gerade weil man so zum Beispiel einfach EPROM im Laden
bestellt, sich schicken lässt und in die Schaltung reinsteckt und gut
ist.
(Da gibt es viele Chips und Einchiplösungen ohne UV-Löschfenster, quasi
unkaputtbar.)
Weiß nicht, ob die heute üblichen Flashspeicher so "nachhaltig" sind wie
die oldschool EPROMS.
ciao
gustav