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