Hallo, in meinem C Programm habe ich zahlreiche Flags, welche nur auf 1 oder 0 gestellt werden. Für jedes Flag nutze ich bisher eine 8 Bit Variable. Nun wird der Programmspeicher meines ATmega88 langsam knapp und ich würde gern für jedes Flag nur 1-Bit verbraten. Wie funktioniert das? mfg
:
Verschoben durch User
1 | u8 bool2; |
2 | #define b2WPan 0
|
3 | #define b2P1an 1
|
4 | #define b2WWan 2
|
5 | #define b2_auto 3
|
6 | #define b2_ds1307 4
|
7 | #define b2statusDisplay 5
|
8 | #define b2status01 6
|
9 | #define b2WPstart 7
|
10 | |
11 | if(gbi(bool2,b2WPan)){....} |
1 | #define Bit0 0
|
2 | #define Bit1 1
|
3 | #define Bit2 2
|
4 | #define Bit3 3
|
5 | #define Bit4 4
|
6 | #define Bit5 5
|
7 | #define Bit6 6
|
8 | #define Bit7 7
|
9 | |
10 | |
11 | uint8_t Flags; |
12 | |
13 | ...
|
14 | |
15 | Flags |= (1<<Bit0); |
16 | |
17 | Flags &= ~((1<<Bit2) | (1<<Bit4)); |
txet schrieb: > Nun wird der Programmspeicher meines ATmega88 langsam knapp ... Der Programmspeicher wird damit aber nicht entlastet, eher der RAM.
kopfkratz Wurde schon genannt hier der gesamte Artikel: http://www.mikrocontroller.net/articles/Bitmanipulation
Als erstes untersuchen wo Sparpotential ist, anstatt blindlinks den Code zu ändern :-)
Ingo Wendler schrieb: > txet schrieb: > >> Nun wird der Programmspeicher meines ATmega88 langsam knapp ... > > Der Programmspeicher wird damit aber nicht entlastet, eher der RAM. Eine grundlegende Fähigkeit des menschlichen Gehirns ist es, über banale Fehler einfach hinwegzuelsen. Eine Fähigkeit mancher Zeitgenossen ist, vortrefflich Korinthen kacken zu können. Johann L. schrieb: > Als erstes untersuchen wo Sparpotential ist, anstatt blindlinks den > Code zu ändern :-) Genau. Guck mal ob jedes int wirklich ein int sein muss. Oder ob das eine oder andere Array auch im Flash untergebracht werden kann. mfg.
Thomas Eckmann schrieb: > Eine grundlegende Fähigkeit des menschlichen Gehirns ist es, über banale > Fehler einfach hinwegzuelsen. so banal finde ich diese Zwischenbemerkung nun auch nicht, sondern schon entscheidend, ob die weitere Diskussion Sinn macht bzw. in eine andere Richtung gelenkt werden sollte!
die drei ??? schrieb: > Thomas Eckmann schrieb: >> Eine grundlegende Fähigkeit des menschlichen Gehirns ist es, über banale >> Fehler einfach hinwegzuelsen. > > so banal finde ich diese Zwischenbemerkung nun auch nicht, sondern schon > entscheidend, ob die weitere Diskussion Sinn macht bzw. in eine andere > Richtung gelenkt werden sollte! Wer sagt denn, daß die Zwischenbemerkung banal ist? Der Fehler ist banal. Denn jeder weiss, was gemeint ist. Nur einer muss mal wieder mit seinem Blabla dazwischenbrabbeln. Ohne auch nur den geringsten konstruktiven Hinweis zu geben. Aber vielleicht war das ja gar kein Fehler. Sondern es ist der Programmspeicher, der auf Grund diverser Initialisierungen langsam überläuft. Man könnte sich aber auch mit einem Atmega328 in beiden Fällen aus der Affäre ziehen. mfg.
:
Bearbeitet durch User
die drei ??? schrieb: > so banal finde ich diese Zwischenbemerkung nun auch nicht, sondern schon > entscheidend, ob die weitere Diskussion Sinn macht bzw. in eine andere > Richtung gelenkt werden sollte! Hatte ich auch so gemeint, wenn nämlich, wie der TE schrieb, der Programmspeicher (Flash) knapp wird, wärend RAM noch ausreichend verfügbar ist, wäre das rauspicken einzelner Bits nämlich eher kontraproduktiv, weil mehr Befehle erforderlich. Andersrum (genug Flash, RAM knapp) ist die Diskussion schon sinnvoll, hatte ich aber so nicht gelesen, vielleicht mein Fehler, dann sorry für den Beitrag.
Rene Schube schrieb: > #define Bit0 0 > #define Bit1 1 > #define Bit2 2 > #define Bit3 3 > #define Bit4 4 > #define Bit5 5 > #define Bit6 6 > #define Bit7 7 > > uint8_t Flags; > > ... > > Flags |= (1<<Bit0); > > Flags &= ~((1<<Bit2) | (1<<Bit4)); Warum macht man das so hässlich?
1 | #define BIT0 0x01
|
2 | #define BIT1 0x02
|
3 | #define BIT2 0x04
|
4 | #define BIT3 0x08
|
5 | #define BIT4 0x10
|
6 | ...
|
7 | #define BIT7 0x80
|
8 | |
9 | ...
|
10 | |
11 | flags |= BIT0; |
12 | |
13 | flags &= ~(BIT2 | BIT4); |
es geht auch
1 | struct ... |
2 | {
|
3 | char one : 1; |
4 | char two : 1; |
5 | ...
|
6 | char eight : 1; |
7 | } flags; |
8 | |
9 | flags.two = 1; |
:
Bearbeitet durch User
hübscher schrieb: > Warum macht man das so hässlich? > es geht auch
1 | > struct ... |
2 | > { |
3 | > char one : 1; |
4 | > char two : 1; |
5 | > ... |
6 | > char eight : 1; |
7 | > } flags; |
8 | >
|
9 | > flags.two = 1; |
Naja, meine C-Erlebnisse sind schon länger her (C interessiert mich auch nicht sonderlich, wenn ich ehrlich bin), aber soweit ich das damals mitbekommen habe, ist ein char normalerweise ein Oktett groß, ergo wäre dieses "struct of 8 chars" dann 8 Bytes groß. Inwieweit dann C noch irgendwas zusätzliches für den Zugriff auf das struct speichern muß weiß ich nicht und hängt wohl auch vom Compiler ab, aber kleiner als 8 Bytes wird's sicher nicht (ausser es gibt Redundazen, die der Compuler wegfegt). Insofern kann man sich dann auch gleich die struct-Konstruktion sparen und und das bool-flag direkt in einem char (oder von mir aus uint8_t oder wie der Kram grade en vogue in C genannt wird) speichern. Falls ich damit falsch liege, würde ich mich aber über Berichtigung freuen. LG, N0R
Norbert M. schrieb: > Naja, meine C-Erlebnisse sind schon länger her Dann solltest du das aber nicht kommentieren. > mitbekommen habe, ist ein char normalerweise ein Oktett groß, ergo wäre > dieses "struct of 8 chars" dann 8 Bytes groß. Nö, ist es nicht. Denn hinter jedem char steht ein klitzekleines ": 1", welches diesen Member als 1 Bit groß ausweist, und der Compiler diese Bits alle zusammenfasst.
Norbert M. schrieb: > Naja, meine C-Erlebnisse sind schon länger her Dann lies mal hier: http://www.c-howto.de/tutorial-strukturierte-datentypen-bitfelder.html Das ": 1" bedeutet, dass jeder Eintrag im Struct nur 1 Bit groß ist.
Karl Heinz schrieb: > Norbert M. schrieb: >> Naja, meine C-Erlebnisse sind schon länger her > Dann solltest du das aber nicht kommentieren. Wer alles weiß braucht nicht zu fragen, das ist klar. > Denn hinter jedem char steht ein klitzekleines ": 1", welches diesen > Member als 1 Bit groß ausweist, und der Compiler diese Bits alle > zusammenfasst. Danke, das wusste ich nicht. Der Doppelpunkt-Operator ist mir eigentlich noch nie über den Weg gelaufen. Naja, wieder wass gelernt. LG, N0R
Ist ja auch ein kein Operator, sondern Teil des Typspezifizierers. : taucht in C(++) nie als Operator auf, sondern nur als - Zusätzlicher Typspezifizierer - switch case: (Quasi Keywordanhängsel) - Als Teil des ternären Operators ?:
Marian B. schrieb: > Ist ja auch ein kein Operator, sondern Teil des Typspezifizierers. > : taucht in C(++) nie als Operator auf, sondern nur als > - switch case: (Quasi Keywordanhängsel) > - Als Teil des ternären Operators ?: Diese beiden Fälle kannte ich, aber das da: > - Zusätzlicher Typspezifizierer war mir eben unbekannt. Danke für den Hinweis (auch wenn ich C hoffentlich nie wieder brauchen werde). LG, N0R Btw, an die Moderation: Jetzt trudeln hier lauter C- und Programmierfragen ein, die haben doch alle nix mit Mikrokontrolleuren oder Digitalelektronik zu tun, kann man die nicht mal in's passende Subforum abschieben?
Moin, Atmega88 ? Nutzt du schon die GPIO Register? Falls nicht kannst du sowas machen
1 | typedef struct { |
2 | bool f0:1; |
3 | bool f1:1; |
4 | bool f2:1; |
5 | bool f3:1; |
6 | bool f4:1; |
7 | bool f5:1; |
8 | bool f6:1; |
9 | bool f7:1; |
10 | } PackedBool; |
11 | |
12 | #define bRequestPortLock1 ( (volatile PackedBool*)(&GPIOR0) )->f0 |
13 | #define bRequestPortLock2 ( (volatile PackedBool*)(&GPIOR0) )->f1 |
Du kannst dann einfach per if darauf zugreifen und sie wie eine normale Variable setzen
1 | if(bRequestPortLock1) |
2 | bRequestPortLock2 = 1; |
Leute, Ihr seid meine Rettung ! Ich bin hier eher durch Zufall "reingelesen" aber schon nach dem Beitrag mit dem Zerlegen einer 8 Bit char Variablen in einzelne Bits und deren Nutzung hat sich nun ein Problem gelöst, dass ich hatte um ein kleines Programm in einen ATtiny13 zu quetschen. In der SPS Programmierung mache ich es jeden Tag, dass einzelne Zustände oder Fehlerbits in einem Merkerbyte gespeichert werden ... Warum kommt man da als KOP (RSLogix5000) Programmierer nicht drauf. Danke! Ihr habt mit dem Hinweis auf die Einzelnutzung von Bits in Variablen mein kleines, privates, Projekt gerettet. Schreibt diesen Hinweis inkl. dem Beispielcode bitte in das AVR C Tutorial als eigenes Kapitel gleich nach der Beschreibung, wie man Register beschreibt und liest. Ich glaube, da passt es am besten hin. Gruß Irfan
Norbert M. schrieb: > Karl Heinz schrieb: >> Norbert M. schrieb: >>> Naja, meine C-Erlebnisse sind schon länger her >> Dann solltest du das aber nicht kommentieren. > > Wer alles weiß braucht nicht zu fragen, das ist klar. Ich sehe nicht, dass du etwas gefragt hättest. Ich sehe, dass du eine falsche Antwort gegeben hast.
Norbert M. schrieb: > Btw, an die Moderation: Jetzt trudeln hier lauter C- und > Programmierfragen ein, die haben doch alle nix mit Mikrokontrolleuren > oder Digitalelektronik zu tun, kann man die nicht mal in's passende > Subforum abschieben? Und welches sollte das sein? Ein explizites Forum für Fragen zu Programmiersprachen / Programmiertechniken gibt es hier nicht. Das Forum GCC ist offiziell für "Fragen zu den GNU-Toolchains" gedacht. Wie man etwas Bestimmtes in der Programmiersprache C umsetzt, gehört da nach offizieller Lesart gar nicht rein - wird aber wohl geduldet.
Das spuckt AVR Studio 5 aus: Device: atmega88p Program: 7330 bytes (89.5% Full) (.text + .data + .bootloader) Data: 120 bytes (11.7% Full) (.data + .bss + .noinit) Der ATmega hat 8 kByte Flash, 1 kByte SRAM und 512 Byte EEPROM Wie es scheint habe ich noch ne Menge an Arbeitsspeicher zur Verfügung. Der Programmspeicher ist fast voll (wobei hier schon einiges Auskommentiert ist, um erstmal weiter arbeiten zu können). Leider fehlt mir das Wissen, was ich im Quelltext ändern muss, sodass der Flash frei wird. Was sind typische "Speicherfresser" ? Wie kann man diese minimieren? Hab bisher immer drauf los programmiert, ohne mir um den Speicher Gedanken gemacht zu haben. Zur Not nehm ich den ATmega168. mfg
Mark Brandis schrieb: > Norbert M. schrieb: >> Btw, an die Moderation: Jetzt trudeln hier lauter C- und >> Programmierfragen ein, die haben doch alle nix mit Mikrokontrolleuren >> oder Digitalelektronik zu tun, kann man die nicht mal in's passende >> Subforum abschieben? > > Und welches sollte das sein? > > Ein explizites Forum für Fragen zu Programmiersprachen / > Programmiertechniken gibt es hier nicht. Das Forum GCC ist offiziell für > "Fragen zu den GNU-Toolchains" gedacht. Wie man etwas Bestimmtes in der > Programmiersprache C umsetzt, gehört da nach offizieller Lesart gar > nicht rein - wird aber wohl geduldet. Die Mods hatten sich vor einiger Zeit geeinigt, das 'GCC' Forum mehr so als allgemeineres C-Forum anzusehen. Denn so klar ist das nicht, wo da jetzt die Grenze ist, was jetzt GCC spezifisch ist und was nicht. Wenn man es genau nimmt, dann wäre dieses Forum wohl recht leer, während sich die C-Fragen im allgemeinen µC-Forum tummeln. Das kann ja auch nicht der Sinn der Sache sein. Auf der anderen Seite will man aber auch nicht für jeden sich irgendwo eröffnenden Teilaspekt ein neues Forum aufmachen. Daher die Bitte an alle: das GCC im Forumstitel nicht allzu eng zu sehen. Seht es mehr als Forum für Programmierprobleme unter Verewndung von C an. Ob das dann GCC spezifisch ist oder nicht, ist dagegen nicht mehr so ganz der entscheidende Punkt.
txet schrieb: > Leider fehlt mir das Wissen, was ich im Quelltext ändern muss, sodass > der Flash frei wird. > Was sind typische "Speicherfresser" ? > Wie kann man diese minimieren? Kann man so nicht sagen. Zeig halt mal was. Dann kann man mal durchsehen und im Map-File nachsehen, welche Funktionen eher lang sind und wo es sich lohnt, da mal über den Quelltext drüber zu sehen, ob man was findet. Ein relativ typischer Fehler, den ich in letzter Zeit zb öfter gesehen habe, ist die unkritische Verwendung von zu großen Datentypen. Also zb ein int in
1 | for( int i = 0; i < 5; i++ ) |
nicht nur dass zb das Inkrementieren eines 16 Bit Wertes länger dauert, als das eines 8 Bit Wertes, erfordert es ja auch mehr Code 16 Bit Werte zu vergleichen, zu inkrementieren, etc. Dabei braucht das in diesem Fall kein Mensch, denn auch mit einem uint8_t kann man wunderbar von 0 bis 4 zählen. Wie gesagt, das ist mir in letzter Zeit des öfteren hier im Forum begegnet. > Hab bisher immer drauf los programmiert, ohne mir um den Speicher > Gedanken gemacht zu haben. > Zur Not nehm ich den ATmega168. Zeig mal den Code. Wunder darf man natürlich keine erwarten, aber das eine oder andere findet man meistens dann doch. PS: den Optimizer hast du aber schon eingeschaltet? Wenn nicht: der bringt dir jetzt erst mal am meisten.
txet schrieb: > Was sind typische "Speicherfresser" ? > Wie kann man diese minimieren? Indem man das Gegenteil von dem tut, was oben empfohlen wurde. ;-) Denn viele Tricks, die RAM einsparen, kosten ROM.
Thomas Eckmann schrieb: > die drei ??? schrieb: >> Thomas Eckmann schrieb: >>> Eine grundlegende Fähigkeit des menschlichen Gehirns ist es, über banale >>> Fehler einfach hinwegzuelsen. >> >> so banal finde ich diese Zwischenbemerkung nun auch nicht, sondern schon >> entscheidend, ob die weitere Diskussion Sinn macht bzw. in eine andere >> Richtung gelenkt werden sollte! txet schrieb: > Wie es scheint habe ich noch ne Menge an Arbeitsspeicher zur Verfügung. > Der Programmspeicher ist fast voll (wobei hier schon einiges > Auskommentiert ist, um erstmal weiter arbeiten zu können). ...ach ne, dann war die bisherige Diskussion vollkommen sinnlos (wie auch schon kurz eingeworfen, aber ignoriert...). Alle bisherigen Vorschläge (Bit-Felder) sind kontraproduktiv, da sie noch mehr Code generieren!
txet schrieb: > Zur Not nehm ich den ATmega168. Das dürfte die einfachste Lösung sein. Optimierung mit -Os hast du aber eingeschaltet, oder? Oliver
Tipp ins Blaue: Schmeiss alles raus, was Fliesskommarechnung und insbesondere Fliesskommaausgabe beinhaltet.
Noch ein Tipp ins Blaue: Schmeiss alle "sprintf()" raus, falls du welche hast, meist geht es deutlich platzsparender "von Hand".
Den Optimizer hab ich auf -O1. Was wird denn bei -Os noch alles "optimiert"? Sind dabei Beeinträchtigungen und Fehlfunktionen möglich? Ja fprintf nutze ich sehr häufig. Genauso strcmp. Denke das wird es sein und das einfachste ist für 10 Cent mehr einen 168er zu nehmen.
txet schrieb: > Ja fprintf nutze ich sehr häufig. Du schreibst mit dem µC in Dateien? txet schrieb: > Denke das wird es sein und das einfachste ist für 10 Cent mehr einen > 168er zu nehmen. Das Einfachste ist nicht immer das Beste. Irgendwann wird der neue Controller auch zu klein. Wenn du jetzt anfängst zu verstehen daß manche Funktionen viel Speicher kosten, und wie man das ggf. vermeiden kann, dann lernst du was für alle nachfolgenden Projekte. Zumal wenig Code oft auch schneller heisst.
txet schrieb: > Den Optimizer hab ich auf -O1. > > Was wird denn bei -Os noch alles "optimiert"? > Sind dabei Beeinträchtigungen und Fehlfunktionen möglich? Wenn du sauber programmiert hast, dann macht es keinen Unterschied. Wenn du dem Compiler aber Schlupflöcher offen gelassen hast, dann optimiert er dir gnadenlos mit allem was er hat und sich im Rahmen der Sprachdefninition bewegt. > Ja fprintf nutze ich sehr häufig. was benutzt du von sprintf? Wie schon gesagt: sprintf ist mächtig - aber auch umfangreich. Wenn du keine allzugroßen Formatieransprüche hast, dann kann man sprintf sicherlich auch durch etwas einfacheres ersetzen. (Ich geh mal davon aus, dass du sprintf meintest und nicht fprintf) > Genauso strcmp. Wenn du Strings vergleichen musst, dann musst du die vergleichen. Da führt kein Weg drann vorbei. strcmp trägt allerdings nicht besonders auf. Fast alle str... Funktionen sind sehr einfach zu implementieren. > Denke das wird es sein und das einfachste ist für 10 Cent mehr einen > 168er zu nehmen. Zeig halt mal. Du könntest dabei noch was lernen.
:
Bearbeitet durch User
Der Optmimierer erzeugt keine "Beeinträchtigungen und Fehlfunktionen". Der deckt höchstens Programmierfehler deinerseits auf. Wenn dein Prgramm aber mit -O1 läuft, läuft es auch mit -Os. Über fprintf solltest du nochmal nachdendenken. Brauchst du das wirklich? Hast du wenigstens die Minimalversion von printf hinzugelinkt? Oliver
txet schrieb: > Ja fprintf nutze ich sehr häufig. > Genauso strcmp. Das Problem sind nicht die Funktionen selber, sondern daß sie exzessiv als Copy&Paste-Monster benutzt werden. Dadurch wird nicht nur der Code sehr unübersichtlich, sondern auch groß und schwer wartbar. Z.B. man hat mehrere Kommandos über die UART auszuwerten. Dann ist es effektiver, man hat ein Array aus Strings und Funtionspointern, was dann mit einem einzigen strcmp durchlaufen wird. Auch sprintf Ausgaben ähneln sich oft, d.h. es reichen wenige, denen man dann Pointer auf die Unterschiede übergibt.
txet schrieb: > Den Optimizer hab ich auf -O1. Für die schwächeren Optimierungen als -Os wüßte ich keinen Grund, sie zu verwenden.
txet schrieb: > Hab bisher immer drauf los programmiert, ohne mir um den Speicher > Gedanken gemacht zu haben. Im Prinzip ist es richtig, erst mal ein Programm zu erstellen welches ein korrektes Ergebnis liefert, und dann erst zu optimieren. Um hier aber optimieren zu können, müssten wir den Sourcecode sehen. Da Du Dich bisher hartnäckig weigerst auch nur eine einzige Zeile vom Code zu zeigen, verstehe ich ehrlich gesagt nicht wie Du Dir das vorstellst, in diesem Forum Hilfe zu finden.
txet schrieb: > Hab bisher immer drauf los programmiert Das ist ausnahmslos immer der falsche Ansatz. Zuerst macht man sich einen Plan, wie sind die Abläufe und Zustände, welche Aufgaben sind zu lösen, welche sind ähnlich oder wiederholen sich und können zusammengefaßt werden. Das spart schonmal ne Menge Arbeit, Fehler und Sackgassen. Ob man dann zuerst das Gerüst oder die Teilaufgaben oder beides schrittweise implementiert, ist nicht so entscheidend. http://de.wikipedia.org/wiki/Top-Down-_und_Bottom-Up-Design
Mark Brandis schrieb: > txet schrieb: >> Hab bisher immer drauf los programmiert, ohne mir um den Speicher >> Gedanken gemacht zu haben. > > Im Prinzip ist es richtig, erst mal ein Programm zu erstellen welches > ein korrektes Ergebnis liefert, und dann erst zu optimieren. Im Prinzip geb ich dir durchaus recht. Allerdings macht es meiner Erfahrung nach einen Unterschied, ob ein Könner 'einfach drauf los programmiert', oder ab das ein Neuling tut. Könner manövrieren sich designmässig seltener in eine Sackgasse und haben auch keine Hemmungen, das bisher entstandene einfach zu verwerfen, wenn sie merken, dass sie sich selbst ins Eck gestellt haben. Neulinge tun das nicht und ziehen schlechtes Design auf Biegen und Brechen durch. D.h. 'optimieren' kann in manchen Fällen auch heißen: Anerkennen das die Methodik schlecht ist und nach algorithmischen Alternativen suchen. Meist ist das sogar die bessere Alternative für Optimierung als irgendwo händeringend nach einzelnen Taktzyklen suchen. Man kann einen Bubble-Sort optimieren so viel man will, gegen andere Sortierverfahren mit logarithmischer Komplexität hat er bis auf ein paar Sonderfälle keine Chance. Selbst wenn die auf Assemblerebene überhaupt nicht optimiert wurden. > Um hier aber optimieren zu können, müssten wir den Sourcecode sehen. Da > Du Dich bisher hartnäckig weigerst auch nur eine einzige Zeile vom Code > zu zeigen, verstehe ich ehrlich gesagt nicht wie Du Dir das vorstellst, > in diesem Forum Hilfe zu finden. Das frag ich mich mittlerweile auch. Vielleicht geht auch wirklich nichts mehr und die Lösung 'größerer µC' ist tatsächlich der vernünftigste Ausweg. Möglich ist es - so ist das ja nicht. Vielleicht liegt aber auch der andere Fall vor, dass es recht einfache Möglichkeiten gibt, 40% vom Code los zu werden. Jeder der Profis hat wohl beide Fälle schon erlebt.
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.