Forum: Mikrocontroller und Digitale Elektronik Sinus Generator in Assembler, Werte übergeben


von Max S. (alfredtorch)



Lesenswert?

Guten Abend,

Ich hab ein Problem mit meinem Assembler Programm.

Die Aufgabenstellung:

Spannungswert über den integrierten AD Wandler (von einem 8051) 
auslesen. Dieser Wert soll mit Hilfe eines AD7801BR die Periode von 
einer Sinus Welle bestimmen. Dass ganze wird auf einer MCLS Platform 
programmiert (Siemens 80535) und mit Hilfe eines Osziloskop validiert 
(siehe Mikro2Schematics.png).  Stichwort Validierung, es geht um ein 
Projekt für ein Lehrveranstaltung die diese Woch endet, Abgabe, 
verdammt!

Bis jetzt, können wir die Spannung berechnen (d.h.: Ausgabe auf LCD) und 
eine Sinus-Welle ausgeben (mit konstanter Periode). Jedoch hier bei gibt 
es schon ein Problem, die Welle ist nicht kontinuierlich und bricht 
immer wieder ab. (siehe Foto ResultOscilM2Constant.jpg)

Wenn man jetzt probiert den BCD packed, oder ASCII Wert von der Spannung 
einzufügen (als Variabel für die Periode - unten im code "magic 
happens"), kommt nicht mehr viel brauchbares raus. (siehe Foto 
ResultOscilM2Var1ano.jpg).

Ich denke es hat was mit der Timing/interruptroutine zutun und/oder mit 
dem Datentyp wenn man den Wert an die Sinus Funktion übergibt.

Jede Hilfe und Anregung (ok, Darstellung ist nicht ganz exakt d.h.: Pin 
Belegung Spannungseingang) wäre nett. Herzlichen Dank

von Ralf (Gast)


Lesenswert?

Also, wenn ich das jetzt richtig verstehe, dann soll das Programm über 
einen externen DAC einen Sinus ausgeben und über den internen ADC die 
ausgegebene Spannung messen? Ist das soweit richtig?

Was mir bei einem extrem schnellen Überflug nicht gefällt ist, dass in 
der Timer-2-ISR Funktionen für's Display aufgerufen werden.

Desweiteren stimmen die Kommentare MINDESTENS der Main-Funktion nicht 
mehr. Außerdem frage ich mich, warum bei der Ausgabe der Wert aus der 
Tabelle gelesen wird, dann der DAC bedient wird, und dann erst der Port 
5 (entgegen dem Port 1 aus dem entsprechenden Kommentar) geschrieben 
wird -> das könnte bereits den Fehler der Ausgabe erklären.

Du solltest das Programm anders gliedern:
Mach eine StateMachine im Main, in der du nacheinander die jeweiligen 
Aufgaben abklapperst, also Sinus ausgeben, Messwert einlesen, 
Displayausgabe. Zeitliche Verzögerungen solltest du mit einem Timer 
machen und nicht über Verzögerungsschleifen (dafür wurden Timer 
schließlich erfunden).
Die StateMachine wechselt immer dann zum nächsten Status, wenn der 
aktuelle abgearbeitet ist.

Ralf

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

1
dastart:    
2
    mov   r5,#28            ; Count variable for 28 table values
3
            mov   dptr,#tabelle     ; Set pointer to start of table
4
5
            mov   a,#00h            ; Offset = 0
6
    
7
main:
8
            mov   r6,a              ; Backup copy of table position
9
            movc  a,@a+dptr         ; Fetch the value
10
            inc   dptr              ; and increment pointer by 1
11
            clr   cs                ; Set CS and Wr to low 
12
            clr   w                 ; 
13
            setb  w                 ; and back to 1, so that a 
14
            setb  cs                ; new value can be accepted
15
            mov   p5,a              ; Output to Port 1
16
            mov   a,r6              ; Restore backup copy of count variable
17
    call  timer                ; short Wait
18
            djnz  r5,main           ; More values in table ?
19
            ;jmp   start             ; If Yes, read next value, otherwise back to the  
20
ret

Du brauchst R6 gar nicht. Du kannst einfach
1
dastart:    mov   dptr,#tabelle     ; Set pointer to start of table
2
            mov   r5,#28
3
daloop:     mov   a,#00h            ; Offset = 0   
4
            movc  a,@a+dptr         ; Fetch the value
5
            inc   dptr              ; and increment pointer by 1
6
            clr   cs                ; Set CS and Wr to low 
7
            clr   w                 ; 
8
            setb  w                 ; and back to 1, so that a 
9
            setb  cs                ; new value can be accepted
10
            mov   p5,a              ; Output to Port 1
11
      call  timer             ; short Wait
12
            djnz  r5,daloop         ; More values in table ?
13
            ret
Aber jetzt das eigentliche Problem:
1
ISR_T2:
2
      CLR   TF2               ;  Reset overflow flag
3
      DJNZ  COMP,IS0          ;  Counter-1, = 0?
4
; ..............................................................  
5
; each second :
6
      MOV   COMP,#40          ; Reload auxiliary counter
7
      CALL  MESURER            ; Fetch converted value, calculate 
8
                              ; voltage value and convert to BCD-value 
9
      CALL  VISUALISER          ; Convert BCD measured value to ASCII
10
                              ; characters an show on LC-Display
11
12
IS0:  RETI
Das geht natürlich gar nicht. Die Routinen MESURER und VISUALISER 
zerstören den Akku, ohne das du ihn vorher mit push acc sicherst und mit 
pop acc am Ende der ISR wiederherstellst. Desgleichen das PSW, das du 
unbedingt retten musst.
Ich halte es beim MCS51 eigentlich so, das ich auch während einer ISR 
auf eine andere Registerbank schalte. Damit musst du die anderen 
Register nicht retten. Für A,B und PSW kommst du aber nicht darum herum.

Ich tendiere auch zu Ralfs Lösung. Nimm den Timer für die Sinuserzeugung 
und die Hauptschleife für alles andere. Dann bist du die zeitraubende 
ISR los und der Sinus steht im Timing sehr stabil.

: Bearbeitet durch User
von Max S. (alfredtorch)


Lesenswert?

Woow, es gibt noch Assembler Helden!

Ja, der interne ADC misst Spannung. Dieser Wert bestimmt die Frequenz 
der Sinus Welle der über die externe DAC geformt wird. Mit der Pin 
Belegung muss ich überprüfen. Mit der StateMachine hatte ich auch schon 
als Gedankensprung, jedoch war es an der Umsetzung gescheitert.

Ich schaue mir dass ganze jetzt noch einmal gründlich und probiere es 
morgen umzusetzen.

Vielen Dank für die Tips.

von Georg G. (df2au)


Lesenswert?

Matthias Sch. schrieb:
>             clr   w                 ;
>             setb  w                 ; and back to 1, so that a
>             setb  cs                ; new value can be accepted
>             mov   p5,a              ; Output to Port 1

Ohne deine Hardware zu kennen, behaupte ich mal, dass diese Zeilen 
falsch sind. Es wäre für mich das erste IC, bei dem die Daten erst 
nach dem /WR Impuls angelegt werden. Sollte es nicht eher so sein:

             clr   w                 ;
             mov   p5,a              ; Output to Port 1
             setb  w                 ; and back to 1, so that a
             setb  cs                ; new value can be accepted

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Naja, während WR schon wahr ist, die Daten anzulegen, ist ja noch 
schlimmer. Max' Originalprogramm funktioniert deswegen, weil ja, ausser 
beim allersten Schreibvorgang, die Daten des vorherigen Umlaufs noch an 
P5 liegen. Deswegen habe ich davon nichts erwähnt.
Dein Vorschlag hingegen geht gar nicht, wenn der DAC die Daten mit der 
fallenden WR Flanke übernimmt. Entweder setzt man P5 also vorher und 
lässt dann CS und WR folgen oder man lässt alles so, wie es ist, dann 
liegen die Daten eben noch länger an P5, bevor sie übernommen werden.

von Max S. (alfredtorch)


Angehängte Dateien:

Lesenswert?

Ich habe heute morgen probiert die Interrupt Routine umzuschreiben rsp. 
zu löschen.
ISR_T2 ist entfernt. Das Programm läuft sogar noch... Es war sogar 
möglich mittels Spannungseintstellung die Ausgabe am Oscilloskop zu 
verändern (NoISR_5Voltage und NoISR_0Voltage). Beim 2 Durchlauf des 
Versuches gab es dann aber nur noch eine konstante Spannung am Ausgang.

Die Sache mit Push/Pop ACC&PSW (in der Interrupt Routine) habe ich auch 
mal getestet: anfangs (1-2s) zufällige Signale aus bis dann der DAC auch 
nur eine konstante Spannung ausgab.

Den Vorschlag den Timer nur für die Sinusfunktion zu benutzen hab ich 
probiert einzubauen. Jedoch, weiss ich nicht wie ich dass umsetzen soll. 
Ich denke Ihr meint so was: http://www.keil.com/forum/8340/. Ich hab mal 
die TC_T2 und RL_T2 auf SET (anstelle von EQU) gesetzt. Jedoch wenn ich 
nur schon die leere Funktion von ISR_T2 mit einem RETI in meinen 
Sinusgenerator (Abschnitt main =>  CALL ISR_T2), gibt das Ding kein 
Lebenszeichen.

Abschnitt: Wartesequenz
Was mir auch ein Rätsel ist, (abgesehen dass meine Sinus-Welle nicht 
kontinuierlich ausgeben wird) dass meine Periode mit der Variabel #ASCII 
sich nicht beeinflussen lässt. Wie kommt das? Dieser Wert wird aber 
problemlos an der LCD-Anzeige anzeigt und geupdated? Und die Benutzung 
von Konstanten (anstelle von #ASCII) beeinflusst die Frequenz der Sinus 
problemlos.

Assembler Experten bin ich nicht, aber jede Anregung, Vorschlag probiere 
ich zu verstehen und um zusetzen nur am nötigen Verständnis hapert es.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Max S. schrieb:

> Was mir auch ein Rätsel ist, (abgesehen dass meine Sinus-Welle nicht
> kontinuierlich ausgeben wird)


Ich würde ehrlich gesagt damit anfangen.

Dazu schmeiss ich das komplette Programm weg, denn da ist irgendwo der 
Wurm drinnen.

Solange ein einfaches Programm keinen durchgehenden Sinus produziert, 
sondern solche Hecker drinnen hat, wie es dein Oszi Bild zeigt, solange 
brauch ich mich um ADC oder LCD-Ausgabe überhaupt nicht kümmern.
Erstmal muss die durch die Tabelle vorgegebene Kurvenform sauber am 
Ausgang aufscheinen. Solange das nicht der Fall ist, ist der Rest 
erstens uninteressant und zweitens eine potentielle Fehlerquelle. Also 
weg damit.
Da dann aber ausser der Tabelle und deren Ausgabe auf den Port nichts 
mehr vom Programm übrig bleibt, kann ich auch gleich ein neues anfangen. 
Dann spar ich mir wenigstens die Arbeit des Abspeckens.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Matthias Sch. schrieb:

> fallenden WR Flanke übernimmt.

Laut Datenblatt müsste es die steigende Flanke sein.

> Entweder setzt man P5 also vorher und
> lässt dann CS und WR folgen oder man lässt alles so, wie es ist, dann
> liegen die Daten eben noch länger an P5, bevor sie übernommen werden.

Ich würds trotzdem richtig stellen.
So wie es jetzt ist, ist die Sache unlogisch.

Die logische Abfolge wäre für mich

* Chip Select auf Low
* Daten anlegen
* Write auf Low
* Write auf High
* Chip Select auf High

Ob man zuerst die Daten anlegt und dann den Chip Select folgen lässt, 
darüber kann man diskutieren. Aber am Write Pin fummle ich erst rum, 
wenn die Daten am Port anliegen. Selbst wenn laut Datenblatt es möglich 
ist, während Write auf Low liegt, die Datenpins nochmal geeändert 
werden. Für mich ist das einfach nur eine Frage des logischen Ablaufs. 
So wie bei einem Datumsstempel: zuerst stell ich das Datum ein und dann 
drücke ich den Stempel nieder. Auch wenn es theoretisch noch möglich 
wäre, das man das Datum einstellt während der Stempel gerade nach unten 
saust.
Im Prinzip funktioniert das ja überall im kompletten Leben gleich: Erst 
werden Dinge in Ruhe eingestellt und dann kommt das Signal "jetzt 
gilts". Schreit man zu früh "Achtung" kommt nur Hektik ins Spiel.

von Peter D. (peda)


Lesenswert?

Deine Programmlogik ist genau falsch rum.
Du machst das Zeitkritische (Sinus) in der langsamen Mainloop und das 
völlig unkritische (ADC, Ausgabe) im Interrupt.
So kann das nix werden, es gehört genau umgekehrt.

Mach erstmal nur das Zeitkritische, also den Sinus-Interrupt.

Wozu brauchst Du überhaupt das krude packed-BCD?

von Wilhelm F. (Gast)


Lesenswert?

Also das wird mit dem integrierten ADC des 80535 schon gehen, daß man 
einen eingelesenen Wert flott an einem I/O-Port wieder aus gibt. Dazu 
würde ich einen Timerinterrupt installieren, der im Interrupt selbst 
wieder eine ADC-Messung startet, und das ADC Ready Flag wird beim 
nächsten Timerinterrupt bestimmt auch da sein.

von Peter D. (peda)


Lesenswert?

Wozu?
Einfach den ADC auf continuous conversion setzen. Und wenn die Mainloop 
den nächsten Wert haben will, einfach nur auslesen.

von Wilhelm F. (Gast)


Lesenswert?

Peter Dannegger schrieb:

> Wozu?
> Einfach den ADC auf continuous conversion setzen. Und wenn die Mainloop
> den nächsten Wert haben will, einfach nur auslesen.

Das kann man natürlich auch, wenn man kein bestimmtes Zeitraster haben 
will. Im Interrupt sofort den Wert auslesen, und an die Ausgabe 
schreiben.

von Karl H. (kbuchegg)


Lesenswert?

Mir gehts wie Peter.

Ich seh den Sinn nicht dahinter, warum man den ADC per Interrupt 
bedienen muss.
Die Komponente, bei der das Timing stimmen muss, das ist die 
Waveform-Generierung. Die kommt in den Interrupt.
Ob der ADC jetzt pro auszugebender Sinus-Periode 10 mal oder 11 mal 
abgefragt wird, ist IMHO dagegen komplett nebensächlich.

von Wilhelm F. (Gast)


Lesenswert?

Karl Heinz schrieb:

> Mir gehts wie Peter.
>
> Ich seh den Sinn nicht dahinter, warum man den ADC per Interrupt
> bedienen muss.
> Die Komponente, bei der das Timing stimmen muss, das ist die
> Waveform-Generierung. Die kommt in den Interrupt.
> Ob der ADC jetzt pro auszugebender Sinus-Periode 10 mal oder 11 mal
> abgefragt wird, ist IMHO dagegen komplett nebensächlich.

Ich meinte halt, den ADC mit einem Timer kombinieren. Da der ADC im 
80535 recht flott ist, könnte es sein, daß er im Continuous Mode den 
Löwenanteil Rechenzeit beansprucht, und die weiteren Vorgänge hemmt.

von Max S. (alfredtorch)


Angehängte Dateien:

Lesenswert?

Gute Nachrichten,

Sinus Funktion in Timer und der Rest (Anzeige und DAC) des Programmes 
wird einfach nebenbei sequentiell ausgeführt. Dass Ganze funktioniert 
(Siehe ISRonSineDAConWave)

Der ADC von dem 8051 scheint wie schon erwähnt ganz flott zusein, so 
dass ich nur einen Interrupt Timer für die zeitkritische Sinus-Welle 
anwende. Das klappt super! Der LCD und die DA Ausgabe am Oszilloskop 
erfühlen Ihre Aufabe bedingungslos. Jedoch, dass einzige was hapert ist 
den Spannungswert vom AD an den Timer zu übergeben.

Ich muss so etwas einbauen: 
http://www.mikrocontroller.net/articles/8051_Timer_0/1#Variable_Timerzeit. 
Jedoch lässt sich die Frequenz nicht von der Spannungswert beeindrucken. 
Wie muss ich die Frequenz definieren? EQU, SET, SFR?

Wie kann ich den Wert des AD (liegt als ASCII, BCD_LSB ... und als ADDAT 
vor) in die Variablen Frequency oder Timer0_repeat_cycles übergeben 
werden? MOV Frequency,#ASCII? (Hat nicht geklappt)

Ist es überhaupt möglich nachträglich den Timer neu zusetzen?

Fast geschafft! Vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

Max S. schrieb:

> erfühlen Ihre Aufabe bedingungslos. Jedoch, dass einzige was hapert ist
> den Spannungswert vom AD an den Timer zu übergeben.

Die übergibst du auch nicht 'an den Timer'.

Der Code, den du dir da besorgt hast, realisiert eine DDS.

Hier ....
1
...
2
        MOV     A,Phase_low
3
        ADD     A,#lo(Frequency)
4
        MOV     Phase_low,A
5
        MOV     A,Phase_high
6
        ADDC    A,#hi(Frequency)
7
        MOV     Phase_high,A      ;16 bit phase-accumulator
8
...
... der Teil ist für die Frequenz zuständig. 'Frequency' muss etwas 
werden, was du während des Programmlaufs verändern kannst.

Leider sagt mir
1
Frequency  sfr  20h
überhaupt nichts, was das sein soll. sfr klingt nach "special function 
register". Scheint wohl so, dass der Original-Autor sich hier ein 
(freies) Register der CPU ausgeborgt und zweckentfremdet hat.

Kann man machen. Allerdings werde ich dann nicht daraus schlau, was die 
Verwendung von #lo bzw. #hi in den Additionen soll.

Ich sprech auch zu wenig 8051, um dir sagen zu können, wie die 
Additionen dann zu verändern sind, wenn man Frequency in Analogie zum 
Phase-Akku in den Speicher legt (bzw. ob das schlau ist). Naiv (und mit 
wenig 8051 Kentnissen) hätte ich das halt so gemacht
1
Phase_low:      DS      1
2
Phase_high:     DS      1
3
Frequ_low:      DS      1
4
Frequ_high:     DS      1

und dann in der Timer-Routine (nicht wortwörtlich nehmen, ich weiss 
nicht wie man beim 8051 die Sache in der Addition anschreiben muss)
1
        MOV     A,Phase_low
2
        ADD     A, "den Inhalt der Speicherzelle Frequ_low"
3
        MOV     Phase_low,A
4
        MOV     A,Phase_high
5
        ADDC    A, "den Inhalt der Speicherzelle Frequ_high"
6
        MOV     Phase_high,A      ;16 bit phase-accumulator

durch Verändern der Werte dieser Speicherzellen ändert sich dann auch 
die Frequenz. Wobei man natürlich die entsprechenden Werte dann auch 
noch korrekt aus dem ADC Wert ausrechnen muss. ABer das ist dann erst 
der zweite Schritt. Der erste ist es, diese 'SChrittweite' bei der 
Generierung der Tabellen-Indizes variabel zu machen.

Und natürlich erst mal vernünftige Werte in Frequ_low bzw. Frequ_high 
eintragen :-)
Aber das sollte ohnehin klar sein.

: Bearbeitet durch User
von Georg G. (df2au)


Lesenswert?

Leider kenne ich deinen Assembler nicht. "Sfr" bedeutet in der X51 Welt 
- wie KHB schon anmerkte - Special Function Register. Die beginnen aber 
alle ab Adresse 0x80. Kann es sein, dass du eigentlich schreiben 
wolltest "Frequency ds 2", also nur Speicherplatz für eine Variable 
reservieren wolltest?

Die Änderung der Frequenz funktioniert nicht, weil du etwas 
verwechselst:
        ADD     A,#lo(Frequency)
        MOV     Phase_low,A
        MOV     A,Phase_high
        ADDC    A,#hi(Frequency)
        MOV     Phase_high,A      ;16 bit phase-accumulator

das # bedeutet "nimm nicht den Inhalt der Variablen sondern die 
Adresse". Du kannst an deinem AD Eingang beliebig drehen, die Adresse 
der Variablen ändert sich dadurch nicht. Probier es mal mit
        ADD     A, BIN_LSB
        MOV     Phase_low,A
        MOV     A,Phase_high
        ADDC    A, BIN_MSB
        MOV     Phase_high,A      ;16 bit phase-accumulator

von Max S. (alfredtorch)


Lesenswert?

Wir haben es hin bekommen die Frequenz mittels Spannungswert zu 
beeinflussen. Ich putze den Quellentext und werde es dann posten! Es ist 
jetzt so, dass der Änderungsbereich sich auf 2 Volt beschränkt d.h.: 
wenn man diesen Wert überschreitet geht es von vorne los. Ich denke dass 
kommt von der Bit-Grösse der Werte (Überlauf, ähnlich wie die untere 
beschriebene 16-Bit-Werte)

Ja, die Sache mit dem SFR kommt von 
http://mcls-modular.de/DE/helpsys/t_as1.htm#Adresszuweisungen . Ich bin 
mir im Klaren dass auch dort steht HINWEIS: Der zugewiesene Wert kann 
nicht nachträglich geändert werden ! Jedoch, in der Not probiert man 
alles aus dass der verdammte variable Wert endlich berücksichtig wird.

Morgen, werde ich deinen Vorschlag mit BIN_MSB/BIN_LSB einfügen. Jedoch, 
hab ich dass in etwas probiert was das Ding nur lahmlegte.

Die Syntaxanweisung #hi(Frequency) und #lo(Frequency) wird durch Makro 
von der     bitfuncs.inc ermöglicht. Mehr dazu hier 
http://mcls-modular.de/DE/helpsys/t_as1.htm#16-Bit-Werten

auf jeden Fall, VIELEN DANK und schönes Fest an euch alle! Heureka!

von Georg G. (df2au)


Lesenswert?

Max S. schrieb:
> Ja, die Sache mit dem SFR kommt von

Mich schaudert... du hast doch DSEG definiert. Überlasse es dem 
Assembler, was er wo hin packt. Wenn du feste Adressen vorgibst, kann 
das furchtbar in die Beinkleider gehen. Da musst du genau wissen, was du 
machst.

> Syntaxanweisung #hi(Frequency) und #lo(Frequency)...
nochmals: # bedeutet, dass du einen festen Wert nimmst. Du willst aber 
den Inhalt einer Variablen nehmen. Da ich nicht weiß, wie dein Makro 
genau aussieht, kann ich dir nicht sagen, ob es reicht, nur das # weg zu 
lassen.

von Karl H. (kbuchegg)


Lesenswert?

Georg G. schrieb:

> Adresse". Du kannst an deinem AD Eingang beliebig drehen, die Adresse
> der Variablen ändert sich dadurch nicht. Probier es mal mit
>         ADD     A, BIN_LSB
>         MOV     Phase_low,A
>         MOV     A,Phase_high
>         ADDC    A, BIN_MSB
>         MOV     Phase_high,A      ;16 bit phase-accumulator

Ah.
Soweit hab ich dann im Code nicht nachgesehen, was er mit dem ADC Wert 
macht. Na wenn der ohnehin schon im Speicher liegt, dann ist das 
natürlich naheliegend, den gleich zu verwenden.

von Karl H. (kbuchegg)


Lesenswert?

Georg G. schrieb:

>> Syntaxanweisung #hi(Frequency) und #lo(Frequency)...
> nochmals: # bedeutet, dass du einen festen Wert nimmst. Du willst aber
> den Inhalt einer Variablen nehmen. Da ich nicht weiß, wie dein Makro
> genau aussieht, kann ich dir nicht sagen, ob es reicht, nur das # weg zu
> lassen.

Ich würde mal sagen: da die beiden Bytes ja sowieso getrennt im Speicher 
ansprechbar sind, braucht die beiden Makros im Grund ja kein Mensch 
mehr.

Mir kommt vor, dass Max schon sehr bei den unterschiedlichen 
Adressierungsmodi schwimmt. Max. Das musst du ändern!

von Georg G. (df2au)


Lesenswert?

Karl Heinz schrieb:
> Mir kommt vor, dass Max schon sehr bei den unterschiedlichen
> Adressierungsmodi schwimmt.

Zu seiner Ehrenrettung wollen wir hier aber festhalten, dass der X51 da 
wirklich etwas trickreich ist.

von Karl H. (kbuchegg)


Lesenswert?

Georg G. schrieb:
> Max S. schrieb:
>> Ja, die Sache mit dem SFR kommt von
>
> Mich schaudert... du hast doch DSEG definiert. Überlasse es dem
> Assembler, was er wo hin packt. Wenn du feste Adressen vorgibst, kann
> das furchtbar in die Beinkleider gehen. Da musst du genau wissen, was du
> machst.


Ich denke, der Originale Autor hat da heftig getrickst.
Es geht ihm in Wirklichkeit gar nicht darum, durch
1
Frequency  sfr  20h
ein Register zur Verwendung zu benutzen.

Die wirkliche Absicht dahinter ist es, einen 16 Bit Wert zur Verfügung 
zu haben (mit dem Wert 00020h), damit er darauf die Makros #lo bzw. #hi 
anwenden kann.

Im Originalcode steht da in Wirklichkeit einfach nur
1
        MOV     A,Phase_low
2
        ADD     A, #20h
3
        MOV     Phase_low,A
4
        MOV     A,Phase_high
5
        ADDC    A, #00h
6
        MOV     Phase_high,A      ;16 bit phase-accumulator

und für die Konstanten, die er so nicht im Code haben wollte (aus 
naheligenden Gründen), hat er sich dann etwas anderes gesucht.
Kommt mir zwar ein bischen komisch vor, dass über sfr zu machen, da gibt 
es doch sicherlich auch andere Möglichkeiten in einem Assembler, eine 16 
Bit Konstante zu definieren, so dass man High- bzw. Lowbyte Makros drauf 
anwenden kann, aber seis drumm.
Das eigentliche Special Function Register an der Adresse 20h wird nie 
auch nur angerührt oder verändert. Im Grunde benutzt er einfach nur 
seine Adresse als Zahlenkonstante.

Das ist meiner Meinung nach der Trick, der hier zum Einsatz gekommen 
ist.

von Georg G. (df2au)


Lesenswert?

Bei dem verwendeten Assembler ist "sfr" nicht ein Special Function 
Register sondern definiert eine Variable an einer festen Adresse.


    SFR

Anweisung, dem genannten <Symbol> die genannte <Adresse> des direkt 
adressierbaren, internen Datenspeichers zuzuweisen.

<Symbol>    SFR      <Adresse>

Und Adressen im Direct Data Bereich sind immer 8 Bit... mehr kann der 
arme X51 nicht direkt adressieren.

Aber egal, Mist war die Deklaration  so oder so.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Karl Heinz schrieb:
> Frequency  sfr  20h

Bei MCS51 beginnt ab Adresse 20h (also direkt über den normalen 4 
Registerbänken) die 'bit addressable area' des RAM, die direkt mit setb 
und clrb angesprochen werden kann. Das wäre der einzige Grund, um 
explizit 20h zu benutzen.

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.