Guten Abend Ich programmiere gerade ein Programm zur Frequenzmessung auf einem PIC18F1220. In meinem Code brauche ich relativ viele Variablen (auch unsigned long) und Zeichenketten. Ich arbeite mit der Student Version des C18 Compilers. Leider habe ich nun keinen Speicherplatz mehr obwohl ich erst 3/4 meines Programms fertig habe. Nun meine Frage: Kann mir jemand ein paar Tipps geben (evt. auch MPLAB Einstellungen) um meinen Code zu optimieren? Diese Zeichenketten brauchen einfach abartig viel Platz -.- Bin für jeden Rat dankbar. Der Code findet ihr im Anhang. p.s Ich weiss der Code ist noch eine Baustelle aber im grossen und ganzen sollte man schnell einen Überblick bekommen. Vielen Dank schon mal!! :)
ROM voll oder RAM? Wenns das RAM ist, dann leg die Strings ins ROM. Ausserdem braucht char*init1 = "Frequenzmesser"; - 2 Bytes RAM für Pointer extra mehr Platz als char init1[] = "Frequenzmesser"; (ggf. ROM Kennzeichnung ergänzen)
Ist wirklich der Speicher zu klein, oder nur ein Array größer als 256 Bytes? Bin da mal auf eine irreführende Fehlermeldung reingefallen.
Kein Name schrieb: > Ist wirklich der Speicher zu klein, oder nur ein Array größer als 256 > > Bytes? Bin da mal auf eine irreführende Fehlermeldung reingefallen Nein es ist schon der Speicher... Jedenfalls Laut .map File. A. K.: Auch mit dieser Variante geht es leider nicht. Habe jetzt schon alles mögliche ausprobiert. Im Moment kommentiere ich einfach alles aus was schon läuft. So kann ich immerhin weitermachen. Aber wäre schade wenn später nicht alles zusammen laufen würde. p.s. Ein anderer PIC kommt leider nicht in Frage. Vielen Dank
schalte doch mal unter view die Memory Usage Gauge an. Da kann man sehr schön beobachten wie die Speicherauslastung ist. Vorausgesetzt das Projekt ist ordentlich angelegt. Holger
Ich habe mir nochmals alles genau angeschaut und der Speicher ist tatsächlich voll... Schon komisch bei einem PIC mit solch einer Peripherie. So kann man ja nicht mal alles verwenden. Naja zum Glück gibt es noch den 1330er. Der ist Pinkompatibel und hat doppelt so viel Speicher.
Roman schrieb: > Ich arbeite mit der Student Version des C18 Compilers. Du könntest auch die "Standard-Eval Version" Version des Compilers nehmen, da sind alle Optimierungen aktiviert, zumindest 60 Tage lang. Und selbst danach findet sich auch ein Weg den Zeitraum zu verlängern (BackUp, Date, VM, Patch)...
Wie andere schon gefragt haben: Welcher Speicher ist voll? Flash oder das RAM? Zu vermuten ist das es sich um RAM handelt, denn ein: char mystring[] = "bla"; landet in beidem. Einmal im Flash, da der Inhalt "bla" für die Intialisierung ja irgendwo stehen muss, und dann einmal im RAM, weil er eben beim Init von der clib dorthin kopiert wird. Je nachdem wie deine Strings angeordnet sind kommt es dann auch noch zu weitrem Flashspeicher-Verbrauch, wenn die Strings nich als ein Block umkopiert werden können, sondern getrennter Code zum umkopieren der einzelnen Strings nötig ist. Am besten ist es die Strings gleich im Flash zu haben: const rom char rom mystring[] = "bla"; erzeugt den String im Flash, und nur dort, und ohne "Overhead". Natürlich muss dann bei der Ausgabe ebenfalls beachtet werden das der String im Flash liegt. Eine Ausgabefunktion für Zeichenketten aus dem ROM auf ein LCD sieht dann z.B. so aus:
1 | void lcd_write_string_rom(unsigned char x, unsigned char y, static const rom char *ldata) |
2 | {
|
3 | lcd_set_cursor(x, y); |
4 | |
5 | while(*ldata) |
6 | {
|
7 | lcd_write_data(*ldata++); |
8 | }
|
9 | }
|
Die lcd_write_data wiedrum arbeitet dann mit normalen unsigned char:
1 | void lcd_write_data(unsigned char dbyte) |
2 | {
|
3 | LCD_RW = LCD_RW_WRITE; |
4 | LCD_DATA_DIR = PORT_OUT; |
5 | LCD_RS = LCD_RS_DATA; |
6 | LCD_DATA_OUT = dbyte; |
7 | |
8 | LCD_E = 1; |
9 | |
10 | pause(4); |
11 | |
12 | LCD_E = 0; |
13 | |
14 | pause(4); |
15 | }
|
Grüße, Chris
> Wie andere schon gefragt haben: Welcher Speicher ist voll? Flash oder > das RAM? Zu vermuten ist das es sich um RAM handelt, denn ein: > > char mystring[] = "bla"; Macht er ja nicht. Er schreibt:
1 | char*init1 = "Frequenzmesser"; |
Der Typ von init1 ist zwar falsch aber solange er den Zeiger nicht dereferenziert ist alles in Ordnung. Und er dereferenzieren tut er ihn nicht. Das macht sein write_string(), wo dann 'rom char*' steht. Ich habe den Code mal durch den C18 gejagt und laut map-file ist es erwartungsgemäß der Flashspeicher der fast voll ist. Die Variablen belegen ja nur gut 30 Bytes. Mir ist das allerdings alles etwas unheimlich. Aus dem Bauch heraus würde ich annehmen dass der AVR-GCC für den gleichen Code nur die Hälfte an Flash brauchen würde. Auffällig ist die Funktion 'Write_Zahl()' die mit 1.2kB erstaunlich groß ist. Würde das gerne auf einen AVR portieren und bei der Gelegenheit auch auf einen M3 aber ich habe gerade etwas zu routen das bis Montag fertig sein muss.
Ah, OK, so genau hatte ich mir das noch nicht angeschaut. Jetzt habe ich dann mal Write_Zahl angesehen. Kein Wunder das die so groß wird. 4 int plus 2 char als Parameter, das erzeugt schon mal fleissig viel Code für den Zugriff auf den Stack. Dazu dann noch die ganzen Deklarationen plus Initilaisierungen am Anfang der Routine. Sehr schlecht das ganze. Auch die Berechnung/Zerlegung der Zahl in einzelne Ziffern, nicht gut. Da wird sehr viel gemacht was so einfach unnötig ist, da gibt es erheblichen Optimierungs-Spielraum. So kann man die Berechnung einer einzelnen Ziffer der Zahl erheblich optimieren. Zuerstmal definiert man sich zwei int oder long int, je nach gewünschtem Zahlenbereich, plus einen char. Dann baut man sich eine divmod Funktion, die eine Art Modulo plus Restwert macht. Hier mal ein Beispiel aus dem Kopf für positive Zahlen bis max. einschliesslich 1000000:
1 | unsigned long value; |
2 | unsigned long divisor; |
3 | unsigned char number; |
4 | |
5 | void divmod(void) |
6 | {
|
7 | number = 0; |
8 | while(value >= divisor) |
9 | {
|
10 | number++; |
11 | value -= divisor; |
12 | }
|
13 | }
|
14 | |
15 | void print_value(unsigned long value_arg) |
16 | {
|
17 | value = value_arg; |
18 | |
19 | // Ausgabe Million
|
20 | divisor = 1000000; |
21 | divmod(); |
22 | put_char_lcd('0' + number); |
23 | |
24 | // Ausgabe Hunderttausender
|
25 | divisor = 100000; |
26 | divmod(); |
27 | put_char_lcd('0' + number); |
28 | |
29 | // Ausgabe Zehntausender
|
30 | divisor = 10000; |
31 | divmod(); |
32 | put_char_lcd('0' + number); |
33 | |
34 | // Ausgabe Tausender
|
35 | divisor = 1000; |
36 | divmod(); |
37 | put_char_lcd('0' + number); |
38 | |
39 | // Ausgabe Hunderter
|
40 | divisor = 100; |
41 | divmod(); |
42 | put_char_lcd('0' + number); |
43 | |
44 | // Ausgabe Zehner
|
45 | divisor = 10; |
46 | divmod(); |
47 | put_char_lcd('0' + number); |
48 | |
49 | // Ausgabe Einer, keine weitere divmod nötig, da "value" bereits < 10
|
50 | put_char_lcd('0' + value); |
51 | }
|
Wenn man auch negative Zahlen will, hat man als Argument dann halt eine signed long, signed int, oder was auch immer. Als erstes packt man dann anstelle der einfahcen Zuweisung "value = value_arg;" folgendes in die Funktion:
1 | if(value_arg < 0) |
2 | {
|
3 | value = -value_arg; |
4 | put_char_lcd('-'); |
5 | }
|
6 | else
|
7 | {
|
8 | value = value_arg; |
9 | put_char_lcd('+'); // oder was auch immer man bei pos. Zahlen will |
10 | }
|
Für der Nummernausgabe der einzelnen Stellen kann man anstelle einer einfache put_char_lcd() auch eine andere Funktion bauen die dann z.B. führende Nullen normal ausgibt, oder als Leerzeichen, oder unterdrückt. Auch dort sollte man das ganze sehr einfach halten, und lediglich die Ziffer als Argument übergeben. Weitere Parameter wie z.B. ausgabe der führenden Nullen, Dezimalpunkt, etc. sollte man dur eine einzelne Funktion einstellen, die wiederum das ganze auch über "globale" Variablen macht. So spart man sich diese elenden Rattenschwänze an Parametern. Wenn man dann eine Zahl ausgeben will muss man einfach eine Folge von Funktionen aufrufen, z.B.:
1 | lcd_set_cursor(cursor_x, cursor_y); |
2 | print_num_style(leadingzero, decimal_at, ...); |
3 | print_value(value); |
Man kann natürlich auch eine "Meta-Funktion" machen die die gesamten Parameter annimmt und dann ihrerseits die einzelne Funktionen aufruft. Am wichtigsten ist aber das benutzen der eigenen divmod Funktion. Das spart schonmal eine Menge Code und somit Platz, weil der ganze unnütze % und / Kram nicht mehr benutzt wird. Grüße, Chris
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.