mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Seltsames Verhalten von IAR EW (8051)


Autor: Mario G. (suicided)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Forum!

Kann sich (bzw. mir)  jemand das Verhalten des IAR Compilers in der 
Angehängten Grafik erklären? -70 + 173 sind bei mir 103 und nicht -153! 
Mach ich da was falsch? ...oder der Compiler ...oder der 8051. Im 
Disassemly sieht das folgendermaßen aus:
// grenzWert = grenzWert + 173; // grenzWert ist ein signed int
  MOV A,R2      ; dort liegt der "low"-Teil von grenzWert
  ADD A,#0xAD   ; 173
  MOV R2,A

Warum missachtet der Compiler bei einer Integer-Variable das Carry-Flag? 
muss ich die 173 irgendwie markieren? Wenn ich mit der Maus drüber gehe, 
ist sie aber ein int.
Als ich den Wert per Hand gerechnet übergeben habe, ging alles wunderbar 
- aber El Commandante will es kompfortabel haben. Leider brauche ich 
dafür einer dieser gräßlichen 16 Bit Variablen auf 'nem Achtbitter. 
(Mach ich nur sehr ungern!)

VG
mario

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
int auf 8Bit-µC ist eigentlich kein Problem, solange kein Interrupt 
zusätzlich an der betreffenden Variable mit rumwerkelt.

Ohne Kenntnis Deines gesamten C-Programmes wird Hilfe schwer.

Autor: Mario G. (suicided)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
char temp;

void wakeOnRadio(char powerMode, signed int grenzWert, unsigned long interval) {
  interval = 32768 * interval / 1000 - 8;
  grenzWert += 173;

    temp = interval;
  asm("       MOV  R7,temp     "); // ST low
    temp = interval >> 8;
  asm("       MOV  R6,temp     "); // ST mid
    temp = interval >> 16;
  asm("       MOV  R5,temp     "); // ST high
    temp = grenzWert;
  asm("       MOV  R4,temp     "); // Grenzwert
    temp = powerMode;
  asm("       MOV  R3,temp     "); // PowerMode

  asm("       CLR  0xC0.7       "); // STIF = 0;
  asm("       SETB 0xA8.5       "); // STIE = 1;
  asm("sleep: MOV  A,0x95       "); // SleepTimer hochzählen
  asm("       ADD  A,R7         ");
  asm("       MOV  R0,A         ");
  asm("       MOV  A,0x96       ");
  asm("       ADDC A,R6         ");
  asm("       MOV  R1,A         ");
  asm("       MOV  A,0x97       ");
  asm("       ADDC A,R5         ");
  asm("       MOV  0x97,A       ");
  asm("       MOV  0x96,R1      ");
  asm("       MOV  0x95,R0      ");

  asm("       MOV  A,#0xFC      "); // SLEEP = (SLEEP & ~0x03) | 0x0_;
  asm("       ANL  A,0xBE       ");
  asm("       ORL  A,R3         ");
  asm("       MOV  0xBE,A       ");
  asm("       NOP               ");
  asm("       NOP               ");
  asm("       NOP               "); 
  asm("       MOV  A,#0x03      "); // if (SLEEP & 0x03)
  asm("       ANL  A,0xBE       ");
  asm("       JZ   end          ");
  asm("       MOV  0x87,#0x01   "); // PCON = 0x01; PowerMode starten
  asm("       MOV  0xE1,#0xE2   "); // RFST = 0xE2; // ISRXON - Radio an
  asm("wffsm: MOV  DPTR,#0xDF39 "); // while (FSMSTATE != 0x06);
  asm("       MOVX A,@DPTR      ");
  asm("       XRL  A,#0x06      ");
  asm("       JNZ  wffsm        ");
  asm("       MOV  R0,#0x00     "); // 128 µs warten = 8 Symbolperioden 
  asm("loop0: NOP               "); // zum mitteln des RSSI-Wertes
  asm("       NOP               ");
  asm("       NOP               ");
  asm("       DJNZ R0,loop0     ");
  asm("       MOV  DPTR,#0xDF07 "); // while ((RSSIL > 0x7F) && (RSSIL < 0xE3))
  asm("       MOVX A,@DPTR      ");
  asm("       CLR  C            ");
  asm("       SUBB A,#0x80      "); // > 7F
  asm("       CLR  C            "); //
  asm("       SUBB A,R4         "); // < E3 
  asm("       JC   sleep        ");
  asm("end:   CLR  0xA8.5       "); // STIE = 0;
}

Bitte nicht darüber beschweren. Was ohne Assembler geht, wird noch 
geändert.
Der µC wird in den Sleep-Modus versetzt und nach der eingestellten 
Intervallzeit wirder aufgeweckt. Dort wird die Empfangsfeldstärke 
überprüft. Ist sie über dem Grenzwert, wird die Funktion verlassen - 
ansonsten beginnt alles von vorn.
Bevor das C-Gerüst da war, hab ich die Werte selbst ausgerechnet und 
vorm Aufruf in die Register geschrieben. Grenzwert ist ein Wert in dBm - 
muss also auch negativ sein. Durch ein Offset kann da leider kein char 
verwendet werden (kleinster Wert -173). Der Aufruf sollte nach 
Möglichkeit aber schon in dBm erfolgen - sonst kann ich mir das Ganze 
auch sparen. Durch das falsche Rechenergebnis funktioniert die Funktion 
nicht mehr richtig, weil der Grenzwert falsch eingestellt wird.
Einen Interrupt gibt es bis auf das Löschen des SleepTimer-Flags nicht. 
Dieser tritt aber immer gleich nach dem wakeUp auf.

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>temp = grenzWert;
>asm("       MOV  R4,temp     "); // Grenzwert

Vielleicht klappt die automatische Typumwandlung von int nach char hier 
nicht. Versuch doch mal grenzWert als int weiter zu behandeln.

Autor: Mario G. (suicided)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Wert von grenzWert stimmt aber schon nach der Addition nicht mehr. 
Durch die Addition ist sichergestellt, dass der Wert in jedem Fall 
zwischen 0 und 255 liegt - die oberen 8 Bit sind also 0. Bei -153 sind 
die oberen acht Bit alle 1 und die unteren genau so, wie bei 103. Das 
passiert, weil der Compiler das Carryflag nicht beachtet.

 -70  1111 1111 1011 1010
+173  0000 0000 1010 1101
-------------------------
=103  0000 0000 0110 0111

raus kommt aber:

-153  1111 1111 0110 0111


Die Integer-Addition wurde also falsch durchgeführt. Das ober Byte muss 
noch mit ADDC "behandelt" werden, selbst wenn man nur 1 addiert. Ein 
Überlaufen des unteren Bytes kann ja immer stattfinden.
Für mich sieht das eindeutig wie ein Compilerfehler aus - oder? Da hat 
wohl jemand zu viel wegoptimiert. Obwohl... die werden ja auch nicht 
verwendet. Durch die ausschließliche Verwendung des unteren Bytes müsste 
ja eigentlich alles in Ordnung sein. Das wirft mich jetzt irgendwie 
zurück... naja... am Montag gehts weiter.

Schönes Wochenende!

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mario G. schrieb:
>     temp = interval;
>   asm("       MOV  R7,temp     "); // ST low
>     temp = interval >> 8;
>   asm("       MOV  R6,temp     "); // ST mid
>     temp = interval >> 16;
>   asm("       MOV  R5,temp     "); // ST high
>     temp = grenzWert;
>   asm("       MOV  R4,temp     "); // Grenzwert
>     temp = powerMode;
>   asm("       MOV  R3,temp     "); // PowerMode

Oh oh.
C und Assembler vermanschen, das sieht aber äußerst gefährlich aus.

Woher weißt Du denn, daß R7, R6 usw. nicht durch den nachfolgenden 
C-Code wieder zerstört werden?

Assember und C zu mischen, ist eigentlich nur was für Profis, die genau 
wissen, wie der Compiler intern tickt. Aber dann schreibt man besser die 
ganze Funktion in Assembler, damit man dem Compiler nicht in die Quere 
kommt.

Und als Profi macht man das nur extrem selten, man kann fast alles in C 
schreiben.


Peter

Autor: Mario G. (suicided)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das war auch mal eine reine Assembler-Funktion die durch den 
"kompfortablen" Aufruf so zerpflückt wurde. Die Register werden nur in 
der Funktion benötigt und auch nur dort verändert. Assembler werde ich 
dennoch beibehalten beim hochzählen des 24 Bit Timerwertes (der muss in 
einer bestimmten Reihenfolge gelesen und auch wieder geschrieben werden) 
und beim checken des RSSI-Wertes (der sonst zwei mal gelesen werden 
müsste). Da ist man mit Assembler ein paar Takte schneller und spart 
sich eine Variable zum Zwischenspeichern. In der Schleife läuft nur mein 
Code - bzw. kein Code, weil der µC schläft. Wird die Schleife verlassen, 
werden die Werte in den Registern nicht mehr benötigt.
Ich gebe aber zu, dass der Code verbesserungswürdig ist. Bisher ist mir 
aber kein anderer schneller Weg bekannt, den Wert aus drei Registern zu 
lesen, hochzuzählen und wieder in diese drei Register zu schreiben. Ich 
brauche jede Mikrosekunde - es geht um Energieeinsparung. :)

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mario G. schrieb:
> Die Register werden nur in
> der Funktion benötigt und auch nur dort verändert.

Aber dann können sie doch längst zerstört sein.
Nach der Zuweisung zu R7, kommt ja wieder eine C-Zeile und die kann es 
sofort zerstören.

Ich kenne den IAR nicht, aber der Keil C51 nimmt z.B. R4,5,6,7 um einen 
long Wert zu speichern.
Eine ASM-Zuweisung zu R7 zerstört dann diese Variable und die folgende 
C-Anweisung verändert wiederum R7 usw.
Nach Deiner ASM/C-Wechselorgie dürfte kein Register mehr einen 
sinnvollen Wert haben.


> Ich
> brauche jede Mikrosekunde - es geht um Energieeinsparung. :)

Das glaub ich Dir nicht.
Du machst zu Anfang ne long Division / 1000, die ist so exorbitant 
teuer, da reißt der Assembler überhaupt nichts mehr raus.


Peter

Autor: Mario G. (suicided)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da kann natürlich auch der Fehler liegen... werde ich mal überprüfen. 
Wie gesagt, die Funktion war mal reines Assembler und wurde auch mit 
Assemblerbefehlen aufgerufen. Da war alles völlig unproblematisch. Die 
übergebenen Werte mussten vorher ausgerechnet werden. Dafür hatte ich 
extra eine GUI geschrieben, die diese teuren Rechnungen vornimmt. So war 
es aber nicht erwünscht.
Die long-Division wird übrigens nur ein mal durchgeführt. Zeitkritisch 
ist nur der Bereich zwischen sleep: und end:.

In c würde das in etwa so aussehen:
void wakeOnRadio(char powerMode, signed int grenzWert, unsigned long interval) {
  long sleepTimer, temp;
  interval = 32768 * interval / 1000;
  grenzWert += 173;

  sleepTimer = ST0;
  temp = ST1;
  sleepTimer |= (temp << 8);
  temp = ST2;
  sleepTimer |= (temp << 16);

  STIF = 0;
  STIM = 1;

  do {
    sleepTimer += interval;
    ST2 = sleepTimer >> 16;
    ST1 = sleepTimer >> 8;
    ST0 = sleepTimer;
    SLEEP = (SLEEP & ~0x03) | powerMode;
    asm("NOP");
    asm("NOP");
    asm("NOP"); // die sind notwendig
    PCON = 0x01; // hier geht der µC schlafen

    RFST = ISRXON; // Radio an
    while (FSMSTATE != 0x06); // auf HF-Kalibrierung warten

    // hier irgendwie 128 µs warten zum Mitteln des RSSI-Wertes

  } while ((RSSIL > 0x7F) && (RSSIL < grenzWert));

  STIM = 0;
}

Das werde ich am Montag mal probieren. Mich hatte da das ständige 
Einlesen des Zählerwertes gestört. Das ist theoretisch aber gar nicht 
nötig. Es reicht ja vielleicht auch, eine "lose" Variable weiter zu 
zählen... mal schauen.

Danke für die Hinweise.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.