Moin, sitze gerade vor einem PIC16F84A welcher mit Assembler
programmiert werden soll. Nun ist die Aufgabe mit dem RISC Befehlssatz
eine Funktion zu schreiben in welcher eine Verzögerung von etwa 1s
vorkommen soll. Bin allerdings etwas aufgeschmissen und weiß nicht genau
wie ich die Verzögerung von etwa 1s hinbekomme.
Vielleicht kann mir ja jemand einen Denkanstoß oder ähnliches geben!
Danke im vor raus!
Marcel H. schrieb:> Bin allerdings etwas aufgeschmissen und weiß nicht genau> wie ich die Verzögerung von etwa 1s hinbekomme.
Du weißt die Taktfrequenz deines PIC?
Dann bastelst du dir Zählschleifen, die den Controller für eine Sekunde
blockieren.
Um auf die "genaue" Zeit zu kommen, musst du natürlich wissen, wie lang
jeder Befehl deiner Schleifen braucht.
Oder du nimmst eine Kamera, die auf eine Uhr (mit Sekundenzeiger) guckt,
die dann meldet, dass der Sekundenzeiger sich bewegt hat.
Für so extrem lange Zeiten nimmt man einen Timerinterrupt, z.B. alle
1ms.
Darin zählt man dann eine Variable runter und wenn sie 0 erreicht, führt
man einen Callback aus oder setzt ein Flag, was dann in der Mainloop die
Aktion ausführt.
Demm in real will man nicht, daß so lange die CPU überhaupt nichts
macht, sondern weiterhin andere Aufgaben bearbeitet.
Eine elegante Lösung ist auch ein Scheduler, in den man z.B. bis zu 10
verzögerte Aufgaben reinstellen kann und der sie dann einmalig oder
zyklisch aufruft. Den Scheduler schreibt man natürlich in C, in
Assembler wird das zu unübersichtlich.
Peter D. schrieb:> Den Scheduler schreibt man natürlich in C, in> Assembler wird das zu unübersichtlich.
Wie so sollte das?
Ist doch nur ne Liste von Calls.
Teo D. schrieb:> Wie so sollte das?> Ist doch nur ne Liste von Calls.
Assembler kennt keine Variablentypen und erst recht keine Structs und
Arrays. Die ganzen Indirectionen, Pointer und Displacements zu Fuß zu
berechnen, ist nur was für extreme Masochisten.
1
typedefvoid(*funcp)(void);
2
typedefuint16_tt_res;
3
4
typedefstruct
5
{
6
uint8_tnext;// next in list or end mark
7
t_resdelta;// delta to previous entry
8
t_resperiod;
9
funcpfunc;
10
}t_ctrl_struct;
11
12
statict_ctrl_structt_ctrl_lst[MAX_SCHEDULE];
13
statict_rest_delay;// count down until next service
Peter D. schrieb:> Denn in real will man nicht, dass so lange die CPU überhaupt nichts> macht, sondern weiterhin andere Aufgaben bearbeitet.
Die Aufgabe klingt nach einer Grundlagenveranstaltung der
Mikrocontroller-Programmierung (zumindest kenne ich das sowohl aus der
Ausbildung, als auch aus dem FH-Studium).
Dabei geht es darum, die Zählparameter einer Warteschleife zu bestimmen,
wenn man die Taktanzahl der benötigten Befehle kennt.
Natürlich kann man es anders machen, aber jemand, der nicht mal solchen
Pippifax hinbekommt, soll einen Scheduler programmieren / verwenden?
Peter D. schrieb:> Assembler kennt keine Variablentypen und erst recht keine Structs und> Arrays.
Du denkst zu komplex.
Mann muss nicht gleich einen ganzen Walt planen, wenn man nur ne
Verkehrsinsel begrünen will.
Teo D. schrieb:> Du denkst zu komplex.
War jetzt auch nur mal als Ausblick gedacht, wenn man erfahrener ist. Ab
der 2. Zeitaufgabe geht es mit dem Scheduler einfacher.
Ein Timerinterrupt hat den Vorteil, daß man die Dauer direkt einstellen
kann und nicht umständlich Befehlszyklen abzählen muß. Man kann also das
Programm leicht erweitern, ohne daß sich die Zeiten durch die
zusätzlichen Befehle verlängern.
Als ich 1990 mit dem 8051 angefangen habe, habe ich sehr schnell schon
die Timer benutzt.
Ich war einfach zu faul, Befehle abzählen zu müssen, denn dabei kann man
sich auch schnell verzählen und die Zeiten stimmen nicht.
Peter D. schrieb:> Als ich 1990 mit dem 8051 angefangen habe, habe ich sehr schnell schon> die Timer benutzt.> Ich war einfach zu faul, Befehle abzählen zu müssen, denn dabei kann man> sich auch schnell verzählen und die Zeiten stimmen nicht.
Timer erfordern i.d.R. auch Interrupts. Die werden vielleicht noch
später im Unterricht behandelt.
Peter D. schrieb:> Teo D. schrieb:>> Du denkst zu komplex.>> War jetzt auch nur mal als Ausblick gedacht, wenn man erfahrener ist. Ab> der 2. Zeitaufgabe geht es mit dem Scheduler einfacher.
Tja, rein aus Forentradition, sollten wir uns jetzt drei Tage und
Nächte, die Köppe darüber einhauen, was denn nu genau als "Scheduler"
gelten darf und ob das ab dem 2. oder 5. Slot sinnvoller ist... ;D
Peter D. schrieb:> War jetzt auch nur mal als Ausblick gedacht, wenn man erfahrener ist.
Nein, mein Lieber. Deine Ratschläge setzen nicht nur einen C-Compiler
voraus, sondern auch noch genügend Platz im Zielcontroller. Bedenke mal,
daß recht viele PIC16 mit 2K an Befehlsworten auskommen müssen und
obendrein auch nur rund 200 Byte an RAM zur Verfügung haben (wenn man
mal durchscheinende RAM-Bereiche und Register abzieht). Und da fängst du
großspurig an von Zeugs zu schreiben, was definitiv zu fett ist für die
Zielhardware. Ein Scheduler in C, und als nächstes kommt ein voll
ausgebautes printf und eine Weile später ein komplettes Linux mit
grafischer Oberfläche?
Man sollte ein bissel auf dem Teppich bleiben. Ich erinnere mich da an
einen Jung-Ex-Kollegen, der bei einem kleinen PIC10 im SOT23 Gehäuse und
nur maximal 512 Befehlsworten meinte, er wüßte keinen Grund, solche
Winzlinge NICHT in C zu programmieren. Nun ja, manche Leute brauchen
unbedingt einen V6, um beim Bäcker um die Ecke ihre Frühstücks-Semmel zu
holen.
Und hier ein kleines Beispiel, wie man sowas in etwa machen kann:
W.S. schrieb:> Man sollte ein bissel auf dem Teppich bleiben.
Man kann auch mit 2kB Flash ne ganze Menge machen, z.B. float habe ich
auch auf dem AT89C2051 benutzt, braucht 1kB Flash.
Atmel hat aber auch die kleinen 8Pinner gut ausgerüstet, z.B. ATtiny85:
8kB Flash, 512B RAM.
Mit PIC kenne ich mich nicht aus.
Ich hab hier noch ein Delaymacro für den 8051 gefunden:
1
mdelay macro value, rx, ry ;cycle, used register(s)
2
local m____1
3
if( value < 5 )
4
rept value
5
nop
6
endm
7
elseif( value < 515 )
8
mov rx, #low((value-1)/2)
9
djnz rx, $
10
if((value and 1) = 0)
11
nop
12
endif
13
else
14
mov rx, #high(value/2+253)
15
mov ry, #low(value/2-2)
16
m____1: djnz ry, m____1
17
djnz rx, m____1
18
if(value and 1)
19
nop
20
endif
21
endif
22
endm
0..4 Zyklen werden mit NOP gemacht, 5..514 Zyklen mit einem DJNZ und
515..65535 Zyklen mit 2 DJNZ.
Der alte A51 Assembler kann leider nur 16Bit rechnen.
W.S. schrieb:> Bedenke mal,> daß recht viele PIC16 mit 2K an Befehlsworten auskommen müssen und> obendrein auch nur rund 200 Byte an RAM zur Verfügung haben (wenn man> mal durchscheinende RAM-Bereiche und Register abzieht).
Der 16F84A hat nur 1K Befehlsworte und 68 Bytes RAM.
W.S. schrieb:> Ich erinnere mich da an einen Jung-Ex-Kollegen, der bei einem kleinen> PIC10 im SOT23 Gehäuse und nur maximal 512 Befehlsworten meinte, er> wüßte keinen Grund, solche Winzlinge NICHT in C zu programmieren.
Recht hatte er.
H. H. schrieb:> Der 16F84A hat nur 1K Befehlsworte und 68 Bytes RAM.
Ist eben noch enger als ich so allgemein geschrieben habe.
Jan schrieb:> Recht hatte er.
Damit daß er es nicht weiß. Tja, das isses wohl.
Peter D. schrieb:> Man kann auch mit 2kB Flash ne ganze Menge machen, z.B. float habe ich> auch auf dem AT89C2051 benutzt,
Benutzt. Hmm. Für die PIC16 hatte ich die GK-Arithmetik selber
geschrieben. Sowohl für 4 Byte GK als auch eine Variante mit 5 Byte.
Damit die Mantisse groß genug ist, um das auch im Frequenzzähler
benutzen zu können.
Ansonsten weiß ich, was man alles in Assembler hinkriegt, auch wenn man
keine Riesenvorräte an RAM+Flash hat. Alles andere sind die leider
vielgehörten Ausreden von C-Programmierern, die nur vertuschen wollen,
daß sie es eben nicht können.
W.S.
H. H. schrieb:> Der 16F84A hat nur 1K Befehlsworte und 68 Bytes RAM.
Ich hab ne kleine Dosierpumpe gebaut, wo ein 16F84 (ohne A!:) mit C
vergewaltigt wurde und da sollte man solche "Klimmzüge" nur in
homöopathischen Dosen anwenden.
Man muss da auch nicht wirklich geizig mit den Ressourcen sein, aber
wenn der "Jagdinstinkt" einmal geweckt ist... :D
Hab kürzlich erst ein neues Update gemacht.
Eigentlich wollte ich ja nur "Globale Dosis-Ende Tasten-Sperre"
einfügen....
/*
48 byte RAM; 864 Words
Globale Dosis-Ende Tasten-Sperre hinzugefügt (+10 Words)
Buzzer Init Macro, auf Funtion Aufruf geändert (-5 Words)
Buzzer(void) ohne Parameter, buzzer Struct auf Global geändert (-16
Words)
Buzzer_Init() an_1bit Parameter raus geworfen (-16 Words)
* Geplant
* 20 Byte RAM noch frei -> Strucht Bitfeld (wieder) auf Char umstellen
-> weniger Code?
* ERLEDIGT 49 Bayte RAM; 858 Words (-6 Words)
*/
Nicht geplant
Auf Assembler umschreiben (-650 Words erwartet) :D
Peter D. schrieb:> Als ich 1990 mit dem 8051 angefangen habe, habe ich sehr schnell> schon die Timer benutzt.> Ich war einfach zu faul, Befehle abzählen zu müssen, denn dabei kann man> sich auch schnell verzählen und die Zeiten stimmen nicht.
Na ja, man kann auch erst mal eine Schleife bis 100000 zählen lassen,
abstoppen und dann korrigiert man diesen Wert damit 1 Sekunde bei
rauskommt.
Man kann auch lernen wie die Inder
https://youtu.be/k_S2gRkUVn8
MaWin schrieb:> Na ja, man kann auch erst mal eine Schleife bis 100000 zählen lassen,> abstoppen und dann korrigiert man diesen Wert damit 1 Sekunde bei> rauskommt.
Wenns wirklich auf ne "genaue" Sekunde ankommt, ist ne Delay-Schleife eh
zu ~100% der falsche Weg.
MaWin schrieb:> Na ja, man kann auch erst mal eine Schleife bis 100000 zählen lassen,> abstoppen und dann korrigiert man diesen Wert damit 1 Sekunde bei> rauskommt.
Das ist mit Abstand das umständlichste, was ich je gehört habe. Gerade
mit ineinander verschachtelten Schleifen wird man damit kaum einen
minimalen Restfehler erzielen.
Peter D. schrieb:> Der alte A51 Assembler kann leider nur 16Bit rechnen.
Das ist doch kein reales Hindernis - man kann auch mit einem
4bit-Prozessor 10stellige Dezimalzahlen verarbeiten, das konnten schon
die ersten Taschenrechner. Zählen ist noch einfacher, gibt es einen
Überlauf zählt man die nächsthöhere Stelle um 1 hoch, usw.
Am Assembler liegt es nicht wenn man das nicht hinbekommt.
Georg
Dauert (n-1)*3 + 2 Zyklen, plus 2 (init) plus 4 (call/return) Zyklen.
Benötigt eine RAM-Adresse d1.
Doppelte Schleife
1
Delay
2
movlw n1
3
movwf d1
4
movlw n2
5
movwf d2
6
Delay_0
7
decfsz d1, f
8
goto $+2
9
decfsz d2, f
10
goto Delay_0
11
return
Dauert ((n1-1)*5) + ((n2-1)*1280) + 4, plus 4 (init) plus 4
(call/return) Zyklen.
Damit kommt man schonmal etwas weiter, braucht dafür 2 Bytes RAM.
Dreifache Schleife
1
Delay
2
movlw n1
3
movwf d1
4
movlw n2
5
movwf d2
6
movlw n3
7
movwf d3
8
Delay_0
9
decfsz d1, f
10
goto $+2
11
decfsz d2, f
12
goto $+2
13
decfsz d3, f
14
goto Delay_0
15
return
Dauert ((n1-1)*7) + ((n2-1)*1792) + ((n3-1)*458752) + 6, plus 6 (init)
plus 4 (call/return) Zyklen.
Braucht 3 Bytes RAM.
Natürlich kann man alle 3 Versionen auch "inline" benutzen, dann
entfällt halt das return und die jeweils 4 Zyklen fehlen.
Und: es geht auch, nur eine Schleife "Delay_0" zu nutzen, aber mehrere
Initialisierungsteile mit verschiedenen Werten, da muss man dann aber
noch jeweils ein goto einrechnen (2 Zyklen).
Sehr kurze Verzögerungen kann man mit nop (1 Zyklus) oder goto $+1 (2
Zyklen) machen.
Bei 4MHz Quarz-Takt ist ein Zyklus = 1 µs, auch ein gern genommenes
Verständnisproblem beim PIC.
Georg schrieb:> Das ist doch kein reales Hindernis
Dann erzähle mir mal, wie ich dem Delay-Macro den Wert 65537 übergebe,
wenn der Assembler nur 16-bittig rechnet.
Peter D. schrieb:> Georg schrieb:>> Das ist doch kein reales Hindernis>> Dann erzähle mir mal, wie ich dem Delay-Macro den Wert 65537 übergebe,> wenn der Assembler nur 16-bittig rechnet.
Na so wie alle das machen. Man kann halt nur einen gewissen Bereich
abdecken und für jeden gibts halt dann ein eigens Macro.
Teo D. schrieb:> Na so wie alle das machen. Man kann halt nur einen gewissen Bereich> abdecken und für jeden gibts halt dann ein eigens Macro.
Man kann sich aber auch die Hose mit der Kneifzange anziehen.
Der AVR-Assembler rechnet intern 64-bittig, d.h. selbst ein Delay von
24h läßt man ihn bequem selber ausrechnen:
Peter D. schrieb:> Dann erzähle mir mal, wie ich dem Delay-Macro den Wert 65537 übergebe,> wenn der Assembler nur 16-bittig rechnet.Peter D. schrieb:> Der AVR-Assembler rechnet intern 64-bittig, d.h. selbst ein Delay von> 24h läßt man ihn bequem selber ausrechnen: .....
Wenn dir langweilig ist, such die einen interessanteren Job!
Bei Sprut findest Du Beispiele für Warteschleifen in Assembler, einfach
etwas weiter runterblättern:
https://www.sprut.de/electronic/pic/programm/lauflicht/lauflich.htm
So ähnlich habe ich das bereits für Projekte genutzt.
Der PIC wartet dann natürlich einfach dumm und ist blockiert für weitere
Aufgaben. Anders ginge es via Interrupt ähnlich dem millis() Befehl bei
Arduino.
Daniel schrieb:> Alter Falter, wer programmiert noch Assembler? Nimm MPLAB IDE und dir> gehts deutlich besser, glaub mir !
Alter Falter, wer kaut noch selber sein täglich Brot? Nimm lieber fertig
gekauten Instant-Brei und dir gehts deutlich besser, glaub mir !
W.S.