Frage

char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

 if (value_to_convert < 10) {
  return (value_to_convert + 48);
 } else {
  char a = value_to_convert / 10;
  double x = fmod((double)value_to_convert, 10.0);
  char b = (char)x;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}

Der Zweck dieser Funktion ist es, einen unsigned char-Wert von 0 bis 99 zu übernehmen und zurückgeben entweder es ist ASCII-äquivalent in dem Fall ist es 0-9 oder einen kleinen globalen Charakter Array manipulieren, die von der anrufenden Code folgenden Funktion verwiesen werden können Fertigstellung.

Ich stelle diese Frage, weil zwei Compiler vom gleichen Hersteller diesen Code auf unterschiedliche Weise interpretieren.

Dieser Code wurde als eine Möglichkeit, geschrieben Adresse Bytes parsen über RS485 in Strings gesendet werden, die leicht an eine Sende-LCD-String-Funktion übergeben werden kann.

Dieser Code wird für die PIC18 Architektur geschrieben (8 Bit uC).

Das Problem ist, dass die Frei / Testversion eines bestimmten Compiler perfekte Assembler-Code generiert, der funktioniert, während eine Leistungseinbußen leiden, aber die bezahlt und angeblich überlegen Compiler generiert Code effizienter zu Lasten der Lage, anhand wobei die Adressen aller meine Bytefeldgruppen verwendet, um die Grafiken auf meinem lCD-Display zu fahren.

Ich weiß, ich bin vielen Schlamm im Wasser unter Verwendung eines proprietären Compiler für eine weniger als typische Architektur setzen, aber ich hoffe, dass jemand da draußen einige Vorschläge hat.

Danke.

War es hilfreich?

Lösung

Modulo und Integer-Division kann sehr sehr teuer werden. Ich habe nicht über Ihre besondere Architektur kennen, aber meine Vermutung ist es teuer dort.

Wenn Sie beide benötigen, Division und Modulo, wählen Sie eine von ihnen und bekommen das andere durch Multiplikation / Differenz.

q =p/10;
r = p - q*10;

Andere Tipps

Ich würde auf jeden Fall vermeiden, auf einem PIC Gleitkomma etwas mit. Und ich würde -Versuchen keine Divisionen zu-verwenden. Wie oft sehen Sie einen Nicht-ASCII-Zeichen auf dem LCD schicken? Können Sie es auf dem LCD-Speicher speichern und dann aufrufen, indem es Speicher Position?

Hier ist, was eine Kluft von 10 wie in meinem Code aussieht, beachten Sie die 17 Zyklen es zu vollenden muss. Denken Sie darüber nach, wie lange das dauern wird, und stellen Sie sicher, dass es nichts anderes warten auf dieser ist.

61:                         q = d2 / 10;
 01520  90482E     mov.b [0x001c+10],0x0000
 01522  FB8000     ze 0x0000,0x0000
 01524  2000A2     mov.w #0xa,0x0004
 01526  090011     repeat #17
 01528  D88002     div.uw 0x0000,0x0004
 0152A  984F00     mov.b 0x0000,[0x001c+8]

Wenn Sie eine Floating-Point etwas in Ihrem Code zu tun, schauen Sie in den Programmspeicher, nachdem Sie es kompiliert haben, auf der symbolischen Register (so können Sie es tatsächlich lesen) und suchen Sie den Punktcode schwimmend, die sein müssen inbegriffen. Sie werden es in der Nähe der Spitze (je nach Code), bald (ish) nach dem _reset Label finden nach oben.

Minen beginnen bei Zeilennummer 223 und die Speicheradresse der 001BC mit _ floatsisf, weiter durch verschiedene zusätzliche Etiketten (_fpack, _divsf3, usw.) und enden in _funpack, letzte Zeile bei 535 und Speicheradresse 0042C. Wenn Sie (42C-1BC = 0x270 =) 624 Bytes verloren Programmplatz, groß, behandeln aber einige Chips haben nur 2k Platz und das ist keine Option.

Stattdessen Punkt schwimmen, wenn es möglich ist, versuchen Festkommaarithmetik zu verwenden, in Basis 2

Soweit nicht in der Lage, all Byte-Arrays in LCD-Display zu verweisen, müssen Sie sicherstellen überprüft, dass Sie nicht eine Null zu senden (das ist eine feine Adresse ist) versucht, aber es ist gestoppt erhalten, indem Code Überprüfung auf das Ende einer ascii Zeichenfolge? (Es ist vor mir passiert ist).

Ich würde wahrscheinlich schreiben, wie:

char byte_to_ascii(char value_to_convert, volatile char *converted_value)
{
 if (value_to_convert < 10) {
  return value_to_convert + '0';
 } else {
  converted_value[0] = (value_to_convert / 10) + '0';
  converted_value[1] = (value_to_convert % 10) + '0';
  return 0;
 }
}

Ist es schlechte Form zu schwimmen, Anruf fmod zu konvertieren, und konvertieren integer zu sein, anstatt nur das% Operator? Ich würde Ja sagen. Es gibt mehr lesbar Möglichkeiten, um ein Programm zu verlangsamen, um etwas Timing Anforderung zu erfüllen, zum Beispiel in einem Schlaf for-Schleife. Egal, welche Compiler oder welche Zwicken von Assembler-Code oder was auch immer, dies ist ein sehr verschleierter Weg, um die Ausführungsgeschwindigkeit des Programms zu kontrollieren, und ich nenne es schlechte Form.

Wenn perfekter Assembler-Code bedeutet, dass es richtig funktioniert, aber es ist noch langsamer als die Umsätze zu Floating-Point und zurück, dann mit ganzen Zahlen und schläft in einem for-Schleife.

Wie für den unvollkommenen Assembler-Code, was ist das Problem? „Auf Kosten der Lage, anhand wobei die Adressen aller meiner Byte-Arrays“? Es sieht aus wie Typ char * ist in Ihrem Code arbeiten, so scheint es, dass Sie alle Ihre Arrays Byte-Adresse kann die Art und Weise der C-Standard sagt Sie können. Was ist das Problem?

Ehrlich gesagt, würde ich sagen, ja ..

Wenn Sie wollen b der Rest sein, verwenden Sie entweder MOD oder Roll-your-own:

char a = value_to_convert / 10;
char b = value_to_convert - (10 * a);

Die Umstellung auf / von Schwimmern ist nie die Art und Weise, Dinge zu tun, es sei denn, Ihre Werte wirklich sind schwebt.

Darüber hinaus würde ich stark an die Konvention explizit Bezug auf Ihre Datentypen als ‚unterzeichnet‘ oder ‚unsigned‘, zu bleiben empfehlen und den nackt ‚char‘ für verlassen, wenn es eigentlich ein Zeichen (Teil einer Zeichenfolge). Sie sind in Rohdaten vorbei, die ich fühle, sollte ein unsigned char sein (vorausgesetzt natürlich, dass die Quelle ist unsigned!). Es ist leicht zu vergessen, wenn etwas unterschrieben werden sollte / unsigned, und mit einem bloßen Zeichen, finden Sie alle Arten von Roll-over-Fehlern erhalten.

Die meisten 8-Bit-Mikros ewig dauern, für ein mehrfach (und mehr als für immer eine Kluft), so versuchen und diese zu minimieren.

Hope, das hilft ..

Der Code scheint zwei sehr verschiedene Dinge zu tun, je nachdem, ob es eine Zahl im Bereich 0-9 oder 10-99 gegeben hat. Aus diesem Grunde würde ich sagen, dass diese Funktion in schlechter Form geschrieben: Ich würde Ihre Funktion in zwei Funktionen aufgeteilt

.

Da wir diskutieren Divisionen um 10 hier ..

Dies ist mein nehmen. Es ist nur einfache Operationen und nicht einmal breite Register benötigen.

unsigned char divide_by_10 (unsigned char value)
{
  unsigned char q;
  q = (value>>1) + (value>>2);
  q += (q>>4);
  q >>= 3;
  value -= (q<<3)+q+q;
  return q+((value+6)>>4);
}

Cheers,   Nils

Es ist typisch für Optimizern unerwünschte thingies von Zeit zu Zeit zu tun, wenn man sich in den Interna stecken.

Ist Ihr converted_value ein globaler Wert oder auf andere Weise in einer solchen Art und Weise zugewiesen, dass der Compiler weiß, dass es nicht zu berühren?

PIC nicht mag Pointer-Arithmetik zu tun.

Als Windows-Programmierer weist darauf hin, verwenden Sie den Mod-Operator (siehe unten).

char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

 if (value_to_convert < 10) {
  return (value_to_convert + 48);
 } else {
  char a = value_to_convert / 10;  
  char b = value_TO_convert%10;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}

Ja, ich glaube, dass Ihre Funktion:
char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

if (value_to_convert < 10) {
    return (value_to_convert + 48);
} else {

char a = value_to_convert / 10;
double x = fmod((double)value_to_convert, 10.0);
char b = (char)x;
a = a + 48;
b = b + 48;
*converted_value = a;
*(converted_value+1) = b;
return 0;

}
}
is in poor form:

Don't use decimal numbers for ASCII chars, use the character, i.e. '@' instead of 0x40.
There is no need for using the fmode function.

Here is my example:
// Assuming 8-bit octet
char value;
char largeValue;
value = value_to_convert / 100;
value += '0';
converted_value[0] = value;
largeValue = value_to_convert - value * 100;
value = largeValue / 10; value += '0';
converted_value[1] = value;
largeValue = largeValue - value * 10; value += '0';
converted_value[2] = value;
converted_value[3] = '\0'; // Null terminator.

Since there are only 3 digits, I decided to unroll the loop. There are no branches to interrupt the prefetching of instructions. No floating point exceptions, just integer arithmetic.

If you leading spaces instead of zeros, you can try this:
value = (value == 0) ? ' ' : value + '0';

Nur ein nitwitt sein, aber mehrere return Aussagen aus der gleichen Funktion können schlechte Form (MISRA) in Betracht gezogen werden.

Auch sind einige der oben angeführten Diskussionen über die Grenze von permature Optimierungen. Einige Aufgaben müssen den Compiler überlassen werden. Doch in einem solchen minimalistisch eingebettet Umgebung können diese Tricks gelten nach wie vor.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top