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


von Mario G. (suicided)


Angehängte Dateien:

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:
1
// grenzWert = grenzWert + 173; // grenzWert ist ein signed int
2
  MOV A,R2      ; dort liegt der "low"-Teil von grenzWert
3
  ADD A,#0xAD   ; 173
4
  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

von Matthias (Gast)


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.

von Mario G. (suicided)


Lesenswert?

1
char temp;
2
3
void wakeOnRadio(char powerMode, signed int grenzWert, unsigned long interval) {
4
  interval = 32768 * interval / 1000 - 8;
5
  grenzWert += 173;
6
7
    temp = interval;
8
  asm("       MOV  R7,temp     "); // ST low
9
    temp = interval >> 8;
10
  asm("       MOV  R6,temp     "); // ST mid
11
    temp = interval >> 16;
12
  asm("       MOV  R5,temp     "); // ST high
13
    temp = grenzWert;
14
  asm("       MOV  R4,temp     "); // Grenzwert
15
    temp = powerMode;
16
  asm("       MOV  R3,temp     "); // PowerMode
17
18
  asm("       CLR  0xC0.7       "); // STIF = 0;
19
  asm("       SETB 0xA8.5       "); // STIE = 1;
20
  asm("sleep: MOV  A,0x95       "); // SleepTimer hochzählen
21
  asm("       ADD  A,R7         ");
22
  asm("       MOV  R0,A         ");
23
  asm("       MOV  A,0x96       ");
24
  asm("       ADDC A,R6         ");
25
  asm("       MOV  R1,A         ");
26
  asm("       MOV  A,0x97       ");
27
  asm("       ADDC A,R5         ");
28
  asm("       MOV  0x97,A       ");
29
  asm("       MOV  0x96,R1      ");
30
  asm("       MOV  0x95,R0      ");
31
32
  asm("       MOV  A,#0xFC      "); // SLEEP = (SLEEP & ~0x03) | 0x0_;
33
  asm("       ANL  A,0xBE       ");
34
  asm("       ORL  A,R3         ");
35
  asm("       MOV  0xBE,A       ");
36
  asm("       NOP               ");
37
  asm("       NOP               ");
38
  asm("       NOP               "); 
39
  asm("       MOV  A,#0x03      "); // if (SLEEP & 0x03)
40
  asm("       ANL  A,0xBE       ");
41
  asm("       JZ   end          ");
42
  asm("       MOV  0x87,#0x01   "); // PCON = 0x01; PowerMode starten
43
  asm("       MOV  0xE1,#0xE2   "); // RFST = 0xE2; // ISRXON - Radio an
44
  asm("wffsm: MOV  DPTR,#0xDF39 "); // while (FSMSTATE != 0x06);
45
  asm("       MOVX A,@DPTR      ");
46
  asm("       XRL  A,#0x06      ");
47
  asm("       JNZ  wffsm        ");
48
  asm("       MOV  R0,#0x00     "); // 128 µs warten = 8 Symbolperioden 
49
  asm("loop0: NOP               "); // zum mitteln des RSSI-Wertes
50
  asm("       NOP               ");
51
  asm("       NOP               ");
52
  asm("       DJNZ R0,loop0     ");
53
  asm("       MOV  DPTR,#0xDF07 "); // while ((RSSIL > 0x7F) && (RSSIL < 0xE3))
54
  asm("       MOVX A,@DPTR      ");
55
  asm("       CLR  C            ");
56
  asm("       SUBB A,#0x80      "); // > 7F
57
  asm("       CLR  C            "); //
58
  asm("       SUBB A,R4         "); // < E3 
59
  asm("       JC   sleep        ");
60
  asm("end:   CLR  0xA8.5       "); // STIE = 0;
61
}

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.

von Matthias (Gast)


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.

von Mario G. (suicided)


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!

von Peter D. (peda)


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

von Mario G. (suicided)


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. :)

von Peter D. (peda)


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

von Mario G. (suicided)


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:
1
void wakeOnRadio(char powerMode, signed int grenzWert, unsigned long interval) {
2
  long sleepTimer, temp;
3
  interval = 32768 * interval / 1000;
4
  grenzWert += 173;
5
6
  sleepTimer = ST0;
7
  temp = ST1;
8
  sleepTimer |= (temp << 8);
9
  temp = ST2;
10
  sleepTimer |= (temp << 16);
11
12
  STIF = 0;
13
  STIM = 1;
14
15
  do {
16
    sleepTimer += interval;
17
    ST2 = sleepTimer >> 16;
18
    ST1 = sleepTimer >> 8;
19
    ST0 = sleepTimer;
20
    SLEEP = (SLEEP & ~0x03) | powerMode;
21
    asm("NOP");
22
    asm("NOP");
23
    asm("NOP"); // die sind notwendig
24
    PCON = 0x01; // hier geht der µC schlafen
25
26
    RFST = ISRXON; // Radio an
27
    while (FSMSTATE != 0x06); // auf HF-Kalibrierung warten
28
29
    // hier irgendwie 128 µs warten zum Mitteln des RSSI-Wertes
30
31
  } while ((RSSIL > 0x7F) && (RSSIL < grenzWert));
32
33
  STIM = 0;
34
}

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.

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.