Forum: Compiler & IDEs Code-Optimierungen gesucht


von Leo B. (luigi)


Angehängte Dateien:

Lesenswert?

Servus,
Also ich hab ein wenig C-Code geschrieben und jetzt versuche ich ihn 
noch zu Optimieren. Und genau das ist der Punkt an dem ich euch um 
Vorschläge bitte, da ich noch nicht sehr lange Programmiere.
Folgender Code soll abwechselnd auf OC0A und OC0B eine Sinus-Halbwelle 
ausgeben. Zusätzlich soll Pin 4 gepollt werden und im Falle eines 
Tastendrucks soll dann Pin 2 abgeschalten werden. Wobei beachtet werden 
muss, dass Pin 4 beim Einschalten gedrückt sein wird und dabei nicht als 
Ausschaltbefehl gewertet werden soll.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
 
4
#define ANZAHL 11
5
6
uint8_t sinustabelle[11] = {0,40,79,116,150,180,206,227,243,252,255};
7
uint8_t counter = 0;                        // Position in der sinustabelle
8
int8_t  summand = 1;                        // Sinustabelle rauf/runter (+1/-1) zählen
9
uint8_t oc_sel = 0;                         // speichert welcher PWM-Ausgang aktiv ist. ( 0->OC0A; 1->OR0B )
10
uint8_t presscount = 0;                     // Dient dem entprellen von S1, sodass nicht das einschalten als ausschalten gewertet wird.
11
uint8_t on = 0;                             // Wird 1 sobald S1 losgelassen wird. Der Chip ist damit offiziel an.
12
13
int main( void ) {
14
  OCR0A=0;                                  // Pulsweite von OC0A auf 0
15
  OCR0B=0;                                  // Pulsweite von OC0B auf 0
16
  TCCR0A = (1<<COM0A1) | (1<<COM0B1) | (1<<WGM01) | (1<<WGM00);  // 2 x non Inverting PWM, Fast PWM Mode
17
  TCCR0B = (1<<CS00);                       // Prescaler: 1
18
  TIMSK0 = (1<<TOIE0);                      // Interrupt on TOP einschalten
19
  DDRB = (1<<PB0) | (1<<PB1) | (1<<PB2);    // PB0 (OC0A), PB1 (OC0B) und PB2 als Ausgang
20
  PORTB = (1<<PB2);                         // PB2 auf 1 setzen, Rest auf 0.
21
  sei();                                    // Interrups aktivieren
22
  
23
  MCUCR = (1<<SE);                          // Sleepmode: idle, + enable
24
25
  while(1)
26
    asm("SLEEP");                           // in Pausen schlafen geht.
27
    
28
  return 0;
29
}
30
31
ISR(TIM0_OVF_vect) {
32
  MCUCR &= ~(1<<SE);                        // Sleepmode disable; Datenblatt S.32, in den letzten 3 Zeilen
33
  counter += summand;                       // Sinustabelle rauf/runter zählen. Kann man ein uin8_t mit einem int8_t addieren?
34
35
  if ( oc_sel )                             // Wenn oc_sel == 1 dann ist OC0B gerade an der Reihe seine Halbwelle abzulaufen
36
    OCR0B = sinustabelle[counter];
37
  else                                      // ansonsten ist OC0A dran.
38
    OCR0A = sinustabelle[counter];
39
40
  if ( counter > ANZAHL-2 )                 // Wenn das letzte Element der Sinustabelle erricht ist,
41
    summand=-1;                             // Sinustabelle wieder zurück laufen
42
  if ( counter < 1 ) {                      // Wenn wieder beim ersten Element der Sinustabelle angekommen,
43
    summand=+1;                             // Sinustabelle wieder vorwärts laufen
44
    oc_sel ^= 0b00000001;                   // Auf anderen OC0_ Ausgang umschalten. Stimmt das? wird hier zwischen 0 und 1 hin und her geschalten? Oder müsste da "0b10000000" stehen?
45
  }
46
  
47
  if ( PINB & (1<<PB4) && on ) {            // Wenn Pin 4 mit V+ verbunden ist und vorher schonmal auf GND war.
48
    presscount++;                           // Zählen wie lange gedrückt wird.
49
    if ( presscount > 250 ) {               // wenn lange genug, dann ist mögliches Prellen auch vorbei (Wichtig beim loslassen des Tasters beim einschalten)
50
        TCCR0A &= ~( (1<<COM0A1) | (1<<COM0B1) );  // PWM-Ausgänge abschlaten
51
        PORTB = 0;                          // Alles aus und damit auch die Stromversorgung
52
53
    }
54
  }
55
  else {
56
    presscount = 0;
57
    on = 1;
58
  }
59
  
60
  MCUCR |= (1<<SE);                         // Sleepmode enable jetzt darf er dann wieder schlafen gehn.
61
  return;                                   // Ist das Nötig?
62
}
Zum Verständnis habe ich noch den Schaltplan mit angehängt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Was heisst den "Optimieren"?

Ist das Programm zu langsam oder zu groß?

So wie das Programm angelegt ist liegt die Sinustabelle im RAM, wo sie 
vom Startup-Code aus dem Flash hin kopiert wird. Sie belegt also Platz 
im RAM und im Flash.

Ist ANZAHL die Größe der Tabelle? Es ginge sowas:
1
...
2
#include <avr/pgmspace.h>
3
4
...
5
6
const uint8_t sinustabelle[] PROGMEM = 
7
{
8
    0, 40, 79, 116, 150, 180, 206, 227, 243, 252, 255
9
};
10
11
#define ANZAHL \
12
    (sizeof (sinustabelle) / sizeof (sinustabelle[0]))
13
14
ISR (...)
15
{
16
   ...
17
   uint8_t sinwert = pgm_read_byte (& sinustabelle[counter]);
18
   ...
19
   OCR0A = sinwert;

Einige Variablen werden nur in der ISR verwendet und müssen nicht global 
sein. Folgendes ist zwar nicht optimaler, aber klarer zum Verständnis, 
weil die Gültigkeitsbereiche nicht großer sind als nötig:
1
ISR (...)
2
{
3
   static uint8_t counter = 0;
4
   static int8_t  summand = 1;
5
   ...

Sleepmode braucht nicht deaktiviert zu werden.

Das wäre eine Sicherheitsmaßnahme, wenn keine Brownout-Erkennung 
aktiviert ist und der µC bei niedriger Spannung wild rumhupfpt und ne 
SLEEP trifft.

TCCR0A kann direkt beschrieben werden mit =, du kennst den Wert der rein 
soll. Schreib ihn rein, das ist klarer und knapper als &= oder |=.

von Leo B. (luigi)


Lesenswert?

Erstmal Danke. Diese Art von Vorschlägen habe ich gesucht. Profikniffe 
halt. Weder ist das Programm zu groß noch zu langsam, aber ich wills 
halt "verschönern" und dabei was lernen.

Johann L. wrote:
> So wie das Programm angelegt ist liegt die Sinustabelle im RAM, wo sie
> vom Startup-Code aus dem Flash hin kopiert wird. Sie belegt also Platz
> im RAM und im Flash.
In welchem Schritt passiert denn das?

> Ist ANZAHL die Größe der Tabelle? Es ginge sowas:
Richtig. Danke für deinen Definitionstip.

>
1
>    uint8_t sinwert = pgm_read_byte (& sinustabelle[counter]);
2
>    ...
3
>    OCR0A = sinwert;
4
>
Könnte ich nicht sinwert sparen, indem ich direkt OCR0A mit 
pgm_read_byte(...) überschriebe?

Und das "&" steht da um die SPeicheraddresse von sinustabelle[..] zu 
bekommen richitg?

> Einige Variablen werden nur in der ISR verwendet und müssen nicht global
> sein. Folgendes ist zwar nicht optimaler, aber klarer zum Verständnis,
> weil die Gültigkeitsbereiche nicht großer sind als nötig:
stehen sie der Funktion dann auch bei jedem Neuaufruf noch mit dem Alten 
wert zur Verfügung? Und würde sie dann nich bei jedem Funktionsaufruf 
neu im RAM angelegt werden?


> Sleepmode braucht nicht deaktiviert zu werden.
>
> Das wäre eine Sicherheitsmaßnahme, wenn keine Brownout-Erkennung
> aktiviert ist und der µC bei niedriger Spannung wild rumhupfpt und ne
> SLEEP trifft.
Das ganze wird zwar von ner Batterie versorgt und Brownout nicht 
eingeplant aktiviert zu sein, aber ich schätze wenn mal ein sleep 
getroffen wird, dann wacht er halt ne Counterrunde später wieder auf.
Danke für diesen Hinweis.

von Benedikt K. (benedikt)


Lesenswert?

Hans Wurst wrote:

>>
1
>>    uint8_t sinwert = pgm_read_byte (& sinustabelle[counter]);
2
>>    ...
3
>>    OCR0A = sinwert;
4
>>
> Könnte ich nicht sinwert sparen, indem ich direkt OCR0A mit
> pgm_read_byte(...) überschriebe?

Nein. Da der AVR keine Special Function Register -> Special Function 
Register Operationen machen kann, sondern erst alles in ein 
Arbeitsregister laden muss, ist sinwert auch bei direktem Beschreiben im 
C Code am Ende im Assembler vorhanden, und zwar als eines der Register 
r0-r31.

> Und das "&" steht da um die SPeicheraddresse von sinustabelle[..] zu
> bekommen richitg?

Ja. Auch das wird vom Compiler passend wegoptimiert, so dass nur noch 
ein Berechnen der Absoluten Adresse und ein Laden aus dem Flash übrig 
bleibt.

von Leo B. (luigi)


Lesenswert?

Benedikt K. wrote:
> Nein. Da der AVR keine Special Function Register -> Special Function
> Register Operationen machen kann, sondern erst alles in ein
> Arbeitsregister laden muss, ist sinwert auch bei direktem Beschreiben im
> C Code am Ende im Assembler vorhanden, und zwar als eines der Register
> r0-r31.

Steckt da eine Atwort auf diese Frage drin?
>> Einige Variablen werden nur in der ISR verwendet und müssen nicht global
>> sein. Folgendes ist zwar nicht optimaler, aber klarer zum Verständnis,
>> weil die Gültigkeitsbereiche nicht großer sind als nötig:
> stehen sie der Funktion dann auch bei jedem Neuaufruf noch mit dem Alten
> wert zur Verfügung? Und würde sie dann nich bei jedem Funktionsaufruf
> neu im RAM angelegt werden?,

Wenn ja, dann versthe ich sie nicht sicher richtig.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hans Wurst wrote:
> Johann L. wrote:
>> So wie das Programm angelegt ist liegt die Sinustabelle im RAM, wo sie
>> vom Startup-Code aus dem Flash hin kopiert wird. Sie belegt also Platz
>> im RAM und im Flash.
> In welchem Schritt passiert denn das?
Das passiert im Startup-Code, wie gesagt. Dieser wird vor main() 
ausgeführt und initialisiert globale Variablen und ruft statische 
Konstruktoren auf (falls vorhanden). Wenn im Progemm ne globale Variable
1
int foo = 1;
definiert ist, dann muss ja irgendwann die 1 in foo reinkommen. Das 
macht der Startup-Code.

>
>> Ist ANZAHL die Größe der Tabelle? Es ginge sowas:
> Richtig. Danke für deinen Definitionstip.
>
>>
1
>>    uint8_t sinwert = pgm_read_byte (& sinustabelle[counter]);
2
>>    ...
3
>>    OCR0A = sinwert;
4
>>
> Könnte ich nicht sinwert sparen, indem ich direkt OCR0A mit
> pgm_read_byte(...) überschriebe?

Ja. Der Compiler sollte die doppelte Ausdrücke die dann da stehen wie 
wie & sinustabelle[counter] rausoptimieren, so daß nur eine Berechnung 
verbleibt (commom sumexpression elimination).

> Und das "&" steht da um die SPeicheraddresse von sinustabelle[..] zu
> bekommen richitg?

pgm_read_byte ist in Inline-Assembler Makro, um Werte aus dem Flash zu 
lesen. Im Endeffekt tut pgm_read_byte(x) das gleiche wie (*(x)), das 
heißt es dereferenziert einen Zeiger. Allerdings nicht im RAM, sondern 
im Flash. Alternativ zu & sinustabelle[counter] kann man auch schreiben 
sinustabelle+counter. Ich bevorzuge die erste Variabte.

>> Einige Variablen werden nur in der ISR verwendet und müssen nicht global
>> sein. Folgendes ist zwar nicht optimaler, aber klarer zum Verständnis,
>> weil die Gültigkeitsbereiche nicht großer sind als nötig:
> stehen sie der Funktion dann auch bei jedem Neuaufruf noch mit dem Alten
> wert zur Verfügung?

Ja.

> Und würde sie dann nich bei jedem Funktionsaufruf
> neu im RAM angelegt werden?

Nein. Sie sind static. Diese Variablen leben nicht im Frame der Funktion 
(also auf dem Stack bzw. in GPRs) sondern dort, wo auch andere globale 
Variablen stehen.

>> Sleepmode braucht nicht deaktiviert zu werden.
>>
>> Das wäre eine Sicherheitsmaßnahme, wenn keine Brownout-Erkennung
>> aktiviert ist und der µC bei niedriger Spannung wild rumhupfpt und ne
>> SLEEP trifft.
> Das ganze wird zwar von ner Batterie versorgt und Brownout nicht
> eingeplant aktiviert zu sein, aber ich schätze wenn mal ein sleep
> getroffen wird, dann wacht er halt ne Counterrunde später wieder auf.

Kommt drauf an wie kritisch das ist. Wenn einem die paar µA die der BOD 
saugt nicht zu viel sind, ist seine Aktivierung kein Luxus.

von Benedikt K. (benedikt)


Lesenswert?

Hans Wurst wrote:

> Wenn ja, dann versthe ich sie nicht sicher richtig.

Ok, dann in Kurzform: Du kannst sinwert einsparen (im C Code), der 
erzeugte Assemblercode bleibt aber gleich.

von Leo B. (luigi)


Lesenswert?

Ah ja. 1000 Dank.
Das denke ich hab ich soweit kapiert. Sehr schön Danke euch Jungs.
Jetz bleiben nurnoch ein paar Fragen die oben im C-Coder versteckt 
waren:

1.
1
...
2
oc_sel ^= 0b00000001;
3
...
Stimmt das? wird hier zwischen 0 und 1 hin und her geschalten? Oder 
müsste da "0b10000000" stehen? Oder noch etwas anderes?

2.
1
...
2
counter += summand;
3
...
Kann man ein uin8_t mit einem int8_t addieren? Bzw. im Falle von -1 
Subtrahieren?

3.
1
...
2
ISR(TIM0_OVF_vect) {
3
   ...
4
   return;  // Ist das return hier Nötig?
5
}
6
...

4. Eine Frage wäre dann doch noch:
>> Und würde sie dann nich bei jedem Funktionsaufruf
>> neu im RAM angelegt werden?
>
>Nein. Sie sind static. Diese Variablen leben nicht im Frame der Funktion
>(also auf dem Stack bzw. in GPRs) sondern dort, wo auch andere globale
>Variablen stehen.

Warum gibt es dann globale Variablen? Oder bleibt eine in einer Funktion 
deklarierte Variable zwar bestehen ist aber nicht lesbar für andere 
Funktionen. Oder muss ich eine Variable die bestehen bleiben soll als 
"static uint8_t ..." einführen und diese ist dann auch für andere 
funktionen Verfügbar?

von Leo B. (luigi)


Lesenswert?

Frage 4 habe ich glaube ich hier
http://home.fhtw-berlin.de/~junghans/cref/CONCEPT/storage_class.html#static
schon gefunden.
Demnach entspricht eine als statik definierte Variable einer globalen 
Variable richtig?
Aber wird sie dann nicht bei jedem Funktionsaufruf neu deklariert, oder 
schreibt der compiler das dann um?

von Karl H. (kbuchegg)


Lesenswert?

Hans Wurst wrote:

> 1.
>
1
...
2
> oc_sel ^= 0b00000001;
3
> ...
4
>
> Stimmt das? wird hier zwischen 0 und 1 hin und her geschalten? Oder
> müsste da "0b10000000" stehen? Oder noch etwas anderes?

Denk nach.
^  bezeichnet die XOR verknüpfung.
   0 ^ 1  ->  1
   1 ^ 1  ->  0
und das alles auf Bit-Ebene.
Technisch gesehen machst du also aus dem Bit 0 eine 1, wenn das Bit 
vorher 0 war und auch wieder zurück. D.h. das Bit 0 toggelt (wechselt) 
ständig zwischen 0 und 1.
Du hast also in oc_sel (da diese Variable ja mit 0 vorinitialisiert 
wurde) diese beiden Bitmuster

    0b00000000
    0b00000001

und ja, das sind die Bitmuster für 0 und 1

wenn du mit 0b10000000 xor-en würdest, dann würde Bit 7 ständig zwischen 
0 und 1 wechseln. Als uint8_t Zahl ausgedrückt wär das aber

    0b00000000   = 0
    0b10000000   = 64

Dezimal gesehen, würde oc_sel also ständig zwischen 0 und 64 wechseln.

Eine andere Möglichkeit, bei der ev. klarer wird was passiert und du 
nicht auf Biteben umpfriemeln musst, wäre zb

   oc_sel = 1 - oc_sel;

ist oc_sel gleich 0, dann bekommt es den Wert 1  ( da: 1 - 0 gleich 1)
ist es 1, dann wird daraus 0  ( da: 1 - 1 gleich 0 )

> 2.
>
1
...
2
> counter += summand;
3
> ...
4
>
> Kann man ein uin8_t mit einem int8_t addieren? Bzw. im Falle von -1
> Subtrahieren?

Sicher kann man. Die C-Regeln sagen in diesem Fall, dass zuerst der 
signed integer in unsigned umgewandelt wird.

> 3.
>
1
...
2
> ISR(TIM0_OVF_vect) {
3
>    ...
4
>    return;  // Ist das return hier Nötig?
5
> }
6
> ...
7
>

Nein. Wenn das return die letzte Anweisung in einer Funktion ist und 
nicht dazu benutzt wird einen Wert zurückzugeben, dann kann man es sich 
auch sparen. Was soll auch sonst passieren, als dass die Funktion zu 
ihrem Aufrufer zurückspringt, wenn die Funktion zu Ende ist.

> 4. Eine Frage wäre dann doch noch:
>>> Und würde sie dann nich bei jedem Funktionsaufruf
>>> neu im RAM angelegt werden?
>>
>>Nein. Sie sind static. Diese Variablen leben nicht im Frame der Funktion
>>(also auf dem Stack bzw. in GPRs) sondern dort, wo auch andere globale
>>Variablen stehen.
>
> Warum gibt es dann globale Variablen?

Du hast das Konzept des Scopes noch nicht verstanden.
Klar könnte man auch globale Variablen machen. Aber dann kann jeder Wald 
und Wiesen-Code auf diese Variablen zugreifen und sie mit Werten 
bestücken wie er lustig ist.
Als static Variablen hingegen, existieren diese Variablen nur in dieser 
Funktion. Andere Code-Stellen sehen diese Variablen nicht und können sie 
sich auch nicht zugänglich machen.
Generell ist es immer gut, wenn man die Sichtbarkeit von Variablen so 
weit wie möglich einschränkt. Damit vermindert man die Gefahr, dass man 
irrtümlich diese Variablen verändert und es vermindert auch das 
Kopfzerbrechen, warum und vor allem wo im Code sich schon wieder wer an 
dieser Variablen vergangen hat, so dass ihr Wert nicht mehr stimmt.

Aber Achtung: Das Schluesselwort 'static' hat mehrere Verwendungszwecke. 
Hier geht es nur darum, dass static auf funktionslokale Variablen dafür 
sorgt, dass diese Variablen beim Verlassen einer Funktion nicht zerstört 
werden sondern bis zum nächsten Funktionsaufruf überleben.

von fritz (Gast)


Lesenswert?

>> Und das "&" steht da um die SPeicheraddresse von sinustabelle[..] zu
>> bekommen richitg?
> pgm_read_byte ist in Inline-Assembler Makro, um Werte aus dem Flash zu
> lesen. Im Endeffekt tut pgm_read_byte(x) das gleiche wie (*(x)), das
> heißt es dereferenziert einen Zeiger. Allerdings nicht im RAM, sondern
> im Flash. Alternativ zu & sinustabelle[counter] kann man auch schreiben
> sinustabelle+counter. Ich bevorzuge die erste Variabte.
>
Heißt das, wenn du nicht
1
pgm_read_byte(x)
sondern
1
(*(x))
schreibst, landet es dann doch im Flash, oder ist das nur wegen der
Übersichtlichkeit/Verständlichkeit?
Ansich entscheidet doch das const für EEPROM oder Flash?

von Karl H. (kbuchegg)


Lesenswert?

fritz wrote:

> Heißt das, wenn du nicht
>
1
pgm_read_byte(x)
> sondern
>
1
(*(x))
> schreibst, landet es dann doch im Flash,

Nein.

SRAM und Flash sind voneinander völlig getrennte Speicherbereiche. Jeder 
Speicherbereich beginnt aber seine Adressierung bei 0. D.h. wenn du nur 
den numerischen Wert einer Speicheradresse hast, kannst du nicht 
entscheiden, ob das jetzt die Adressierung ins SRAM oder ins Flash sein 
soll. Daher muss man nachhelfen.
Ist nichts weiter angegeben, dann geht der COmpiler davon aus, dass die 
Adresse eine SRAM Adresse ist. Das kommt dann bei *x zum Zug
Will man aus dem Flash lesen, dann muss man das mitteilen. pgm_read_byte 
weiss wie man aus dem Flash an einer bestimmten Speicheradresse lesen 
muss. Nur muss man dieser Funktion dann auch diese Adresse übergeben.

Thats all.

> Ansich entscheidet doch das const für EEPROM oder Flash?

Ist compilerabhängig. Beim AVR-gcc muss man mit geeigneten Mitteln 
sicherstellen, dass Konstanten im Flash landen. Ansonsten landet alles 
letztendlich im SRAM

von Johannes M. (johnny-m)


Lesenswert?

fritz wrote:
> Heißt das, wenn du nicht
>
1
pgm_read_byte(x)
> sondern
>
1
(*(x))
> schreibst, landet es dann doch im Flash, oder ist das nur wegen der
> Übersichtlichkeit/Verständlichkeit?
Landen tut da gar nichts, weil pgm_read_byte etwas aus dem Flash liest 
und nichts hineinschreibt.

Und nein, es ist nicht nur wegen der Übersichtlichkeit. Die 
Harvard-Architektur der AVRs hat getrennte Busse für Daten- und 
Programmspeicher. Der Zugriff auf Daten im Programmspeicher (Flash) 
geschieht völlig anders als der auf Daten im SRAM, nämlich über 
spezielle Assembler-Befehle.

Ein einfacher Zeiger zeigt immer ins SRAM. Deshalb muss man, um an Daten 
im Flash zu kommen, die speziellen Makros verwenden. Beim EEPROM sieht 
das noch ganz anders aus, da dieses von der µC-Hardware nicht wie ein 
Speicher behandelt wird, sondern wie eine Peripheriekomponente, weshalb 
der Zugriff hier über I/O-Register geschieht.

> Ansich entscheidet doch das const für EEPROM oder Flash?
const besagt zunächst nichts über den Speicherort, sondern besagt 
nur, dass eine so deklarierte Variable eben konstant ist und vom 
Programm nicht verändert werden kann/darf.

von Johannes M. (johnny-m)


Lesenswert?

Karl heinz Buchegger wrote:
>> Ansich entscheidet doch das const für EEPROM oder Flash?
>
> Ist compilerabhängig. Beim AVR-gcc muss man mit geeigneten Mitteln
> sicherstellen, dass Konstanten im Flash landen. Ansonsten landet *alles*
> letztendlich im SRAM
Stimmt natürlich. Manche Compiler machen solche Sachen automatisch. Aber 
zunächst hat const eben nur o.g. Bedeutung. Manche Compiler benutzen 
spezielle Speicherklassenqualifizierer (CVAVR z.B. flash bzw. 
eeprom). Die so deklarierten Daten sind dann ohne Makros verarbeitbar, 
die korrekte Umsetzung erledigt der Compiler im Hintergrund.

BTW:
Welcome back!

von Leo B. (luigi)


Lesenswert?

Hey danke euch. Ich glaube ich verstehe sogar so langsam von was ihr 
redet.
Aber nebenbei hab ich mal noch so ne Frage. Ich will meinen mC einfach n 
paar mikrosekunden blockieren bevor er weiter macht. Nur warum 
funktioniert das nicht:
1
for (uint16_t i = 0 ; i < 3000 ; i++);
Da kommt die Fehlermeldung:
../test.c:22: error: 'for' loop initial declaration used outside C99 
mode

von yalu (Gast)


Lesenswert?

Variablendeklarationen in For-Schleifenköpfen waren in früheren
C-Standards noch nicht erlaubt, erst in C99. Mit der Option

  -std=c99

aktivierst du den C99-Modus des Compilers, und die Fehlermeldung
verschwindet. Der Compiler nimmt es dann mit dem Standard aber sehr
genau und beanstandet das
1
  asm("SLEEP");

das du nun durch ein
1
  __asm("SLEEP");

ersetzen musst.

Deine For-Schleife ist allerdings nicht geeignet, um Wartezeiten ins
Programm einzufügen, da sie aus Sicht des Compilers keinen Effekt hat
und deswegen wegoptimiert wird. Speziell dafür vorgesehen sind aber die
Funktionen _delay_ms und _delay_us aus der der AVR-Libc:

http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

yalu wrote:

> Der Compiler nimmt es dann mit dem Standard aber sehr
> genau und beanstandet das
[...]

Wenn man es etwas laxer haben möchte, kann man -std=gnu99 benutzen.

Die Voreinstellung entspricht -std=gnu89.

von Leo B. (luigi)


Lesenswert?

Ah ja super. Die "Delay.h" kenne ich sogar, ich wollte sich rauslassen 
um Speicher zu Sparen (und weil ich nicht kapier was die macht), aber 
jetzt stelle ich fest, dass sie weniger speicher braucht. Also gekauft^^
Danke.

Aber dank einigen Codeerweiterungen geht mir jetzt der Speicher im SRAM 
aus. 64byte is halt wenig. jetz muss ich irgendwie ein struct erstellen. 
Das GCC-Tutorial finde ich da ja schon ganz schön aber Speicherklassen ( 
so nennt man doch static und co oder?) lassen sich da nicht mehr 
verwenden oder?


Ich dachte mir das so:
1
struct {
2
    unsigned int counter:4;
3
    signed int summand:2;
4
    unsigned int oc_sel:1;
5
  } byte0 ;
2 neue Fragen. Kann ich denen beim definieren schon einen wert 
verpassen? also summand = 1 oder sowas?
Und 2. stimmts dass summand 2 Bit baraucht um 1 und -1 zu speichern?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hans Wurst wrote:

> 2 neue Fragen. Kann ich denen beim definieren schon einen wert
> verpassen? also summand = 1 oder sowas?

Ja.

> Und 2. stimmts dass summand 2 Bit baraucht um 1 und -1 zu speichern?

Ja. In GNU-C etwa so:
1
... byte0 = { .counter=1, ...};

Ansonsten auch ohne die Felder zu nennen, dann aber die Reigenfolge 
beachten!

Aber wenn es dir um Code geht ist es wohl günstiger, sowas zu machen:
1
   foo += byte0.summand ? 1 : -1;

von Leo B. (luigi)


Lesenswert?

>> 2 neue Fragen. Kann ich denen beim definieren schon einen wert
>> verpassen? also summand = 1 oder sowas?
>
> Ja.
>
Wie? etwa so?
1
struct {
2
    unsigned int counter:4 = 0;
3
    signed int summand:2 = 1;
4
    unsigned int oc_sel:1 = 0;
5
  } byte0 ;


1
 ... byte0 = { .counter=1, ...};
So kann ich den Feldern nach dem definieren einen Wert zuweisen oder?
Aber ich wollte es ja beim definieren schon machen.


> Aber wenn es dir um Code geht ist es wohl günstiger, sowas zu machen:
1
    foo += byte0.summand ? 1 : -1;
Was macht diese Zeile?

von Karl H. (kbuchegg)


Lesenswert?

Hans Wurst wrote:
> Wie? etwa so?
>
1
> struct {
2
>     unsigned int counter:4 = 0;
3
>     signed int summand:2 = 1;
4
>     unsigned int oc_sel:1 = 0;
5
>   } byte0 ;
6
>
>
>
>
1
>  ... byte0 = { .counter=1, ...};
2
>
> So kann ich den Feldern nach dem definieren einen Wert zuweisen oder?
> Aber ich wollte es ja beim definieren schon machen.

Machst du doch. Komplett ausgeschrieben
1
struct {
2
     unsigned int counter:4;
3
     signed int summand:2;
4
     unsigned int oc_sel:1;
5
} byte0 = { .counter=1, .summand=0, .oc_sel=0 };

Das man hier die Strukturkomponenten benennen kann, ist eine GNU 
Erweiterung (damit man nicht auf die Reihenfolge achten muss). In reinem 
Standard-C ist die Reihenfolge ganz einfach die Deklarationsreihenfolge
1
struct {
2
     unsigned int counter:4;
3
     signed int summand:2;
4
     unsigned int oc_sel:1;
5
} byte0 = { 1, 0, 0 };

>
1
>     foo += byte0.summand ? 1 : -1;
2
>
> Was macht diese Zeile?

Beitrag "WAS macht diese Zeile?"

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hans Wurst schrob:
>>> 2 neue Fragen. Kann ich denen beim definieren schon einen wert
>>> verpassen? also summand = 1 oder sowas?
>>
>> Ja.
>>
> Wie? etwa so?

Waren oben wohl zu viele Pünktchen...
1
struct {
2
    unsigned counter:4;
3
    signed   summand:2;
4
    unsigned oc_sel:1;
5
} byte0 = 
6
{
7
    .counter = 0,
8
    .summand = 1,
9
    .oc_sel = 0
10
};
11
12
>> Aber wenn es dir um Code geht ist es wohl günstiger, sowas zu machen:
13
>  [c]
14
>     foo += byte0.summand ? 1 : -1;
15
>
> Was macht diese Zeile?

Hier helfen die C-Grundlagen! Semantisch fast gleichbedeutend mit
1
     if (byte0.summand)
2
         foo = foo + 1;
3
     else
4
         foo = foo - 1;

von Leo B. (luigi)


Lesenswert?

Ah eure antworten kombiniert und ich verstehe was gemeint ist. Sehr 
cool. Danke!

die Pünktchen habe ich falsch interpretiert. Aber jetzt verstehe ich 
das. Danke.

Und dann meintest du mit
> Aber wenn es dir um Code geht ist es wohl günstiger, sowas zu machen:
also, dass es den kürzeren Code gabe wenn ich counter auf diese weise 
zählen lasse, anstatt meiner signed int.
Oder dass wenigstens nur 1 bit nicht 2 gespeichert werden müssen.

Das werde ich natürlich anwenden. Danke.

Wieder 2 Fragen sind aber noch offen.
1. Speicherklassen wie static und const lasen sich in structs nicht 
verwenden richtig?
2. das struct muss ich (zumindest bei meiner Simulation von Studio 4) 
ganz am Anfang global definieren.
schreibe ich
1
int main ( void ) {
2
  ...
3
}
4
ISR(TIM0_OVF_vect) {
5
  struct {
6
    unsigned int counter:4;
7
    signed int summand:2;
8
    unsigned int oc_sel:1;
9
  } byte0 = { 0, 1, 0 );
10
  ...
11
}
sind die Variablen irgendwie nicht schreib oder lesbar. Es gibt zwar 
keine Fehlermeldung, aber das Programm arbeitet auch nicht.
Kann das stimmen?
PS: Die variablen werden auch NUR in der Interruptroutine gelesen und 
geschreiben.

von Karl H. (kbuchegg)


Lesenswert?

Eine Frage noch:

Hat es einen Grund, warum du deinem Programm die Bürde mit der 
Bitpfriemelei innerhalb der Struktur aufhalst?

Du bist dir hoffentlich im klaren, dass die Bitextraktion aus der 
Struktur

* mehr Code erfordert
* langsamer ist

Wenn du also die paar Bytes für 'normale' Variablen verschmerzen kannst, 
fährst du mit einer nicht-bitgepfriemelten Struktur besser.

von Karl H. (kbuchegg)


Lesenswert?

Hans Wurst wrote:
> Wieder 2 Fragen sind aber noch offen.
> 1. Speicherklassen wie static und const lasen sich in structs nicht
> verwenden richtig?

Da scheint ein Misverständnis vorzuliegen.

Mit einer struct Deklaration zeigst du dem Compiler einfach nur, wie ein 
bestimmter, von dir gewünschter, Datentyp aussieht.
1
struct MeinTyp
2
{
3
  int a;
4
  int b;
5
};

Das ist einfach nur die Deklaration, was gemeint ist, wenn eine Variable 
vom Typ MeinTyp ist. Damit weiß der Compiler was gemeint ist, wenn du 
dann tatsächlich eine Varaible davon anlegst
1
  struct MeinTyp Variable1;

und ja. So wie bei jeder anderen Variablen kann die Variable static oder 
const gemacht werden.
1
  static struct MeinTyp Variable2;
2
  const struct MeinTyp Var3 = { 2, 3 };

Was du hast, ist eine Kombination aus mehreren C-Dingen.
Zum einen kann gleichzeitig in der Deklaration einer Struct auch eine 
Variablendefinition erfolgen
1
struct MeinTyp
2
{
3
  int a;
4
  int b;
5
} Variable4;

und zum anderen kann dann in diesen Fällen due Struct als anonyme Struct 
(also ohne Datentypnamen) ausgeführt werden.
1
struct
2
{
3
  int a;
4
  int b;
5
} Variable4;

Aber der springende Punkt ist immer noch:
Das eine ist eine Deklaration (also die Beschreibung wie etwas aussieht) 
und das andere eine Definition (also das Erzeugen des tatsächlichen 
'Objekts' anhand der Beschreibung.

Der Unterschied ist ungefähr so, wie der Unterschied zwischen Bauplan 
und dem nach diesem Bauplan gebauten Haus. Es macht dann keinen Sinn, 
wenn du fragst, wie du den Bauplan 'mit Farbe streichen kannst', denn in 
Wirklichkeit willst du ja das tatsächliche Haus streichen und wenn 
mehrere Häuser anhand desselben Plans gebaut werden, dann soll natürlich 
jedes Haus seine eigene Farbe bekommen können

> sind die Variablen irgendwie nicht schreib oder lesbar. Es gibt zwar
> keine Fehlermeldung, aber das Programm arbeitet auch nicht.
> Kann das stimmen?

Nein, das kann nicht stimmen. Da ist dann noch was anderes faul

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger wrote:

> Nein, das kann nicht stimmen. Da ist dann noch was anderes faul

Im Speziellen musst du dir darüber im klaren sein, dass funktionslokale 
Variablen beim Betreten einer Funktion erzeugt und beim Verlassen einer 
Funktion zerstört werden (es sei denn sie sind static)

von yalu (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Hat es einen Grund, warum du deinem Programm die Bürde mit der
> Bitpfriemelei innerhalb der Struktur aufhalst?

In dem Post, wo er nach der Strukturinitialisierung fragte, schrieb er:

> Aber dank einigen Codeerweiterungen geht mir jetzt der Speicher im
> SRAM aus. 64byte is halt wenig.

Programmspeicher hat er wohl noch übrig.

@Hans Wurst:

Nur solltest du, um den RAM-Verbrauch zu optimieren, die Elemente der
Struktur nicht als int sondern als char deklarieren, da nur dann die
gesamte Struktur tatsächlich nur 1 Byte belegt.

> 1. Speicherklassen wie static und const lasen sich in structs nicht
> verwenden richtig?

Mit static kann nur die gesamte Strukturvariable deklariert werden, da
die Elemente einer Struktur immer als zusammenhängender Block
gespeichert werden.

const ist keine Speicherklasse, sondern ein Qualifier (auf deutsch
Qualifizierer?). Und Qualifier können auch auf einzelne Strukturelemente
angewendet werden.

> 2. das struct muss ich (zumindest bei meiner Simulation von Studio 4)
> ganz am Anfang global definieren. schreibe ich
> ...
> sind die Variablen irgendwie nicht schreib oder lesbar. Es gibt zwar
> keine Fehlermeldung, aber das Programm arbeitet auch nicht. Kann das
> stimmen?

Ja, weil die Strukturvariable lokal, aber nicht static ist. Dadurch
geht ihr Inhalt zwischen zwei Interrupts möglicherweise verloren.

von Leo B. (luigi)


Lesenswert?

yalu wrote:
> Programmspeicher hat er wohl noch übrig.
Jo da hab ich noch 66% frei.

> Nur solltest du, um den RAM-Verbrauch zu optimieren, die Elemente der
> Struktur nicht als int sondern als char deklarieren, da nur dann die
> gesamte Struktur tatsächlich nur 1 Byte belegt.
Ich dachte die Zahl nach dem Doppelpunkt gibt die anzahl der Bytes an, 
die die Variable bekommt?
damit wäre ja eigentlich (kommt mir erst jetzt) int oder char völlig 
überflüssig, da dies eine widersprüchliche Doppeldefinition der Länge 
ist.

>> 2. das struct muss ich (zumindest bei meiner Simulation von Studio 4)
>> ganz am Anfang global definieren. schreibe ich
>> ...
>> sind die Variablen irgendwie nicht schreib oder lesbar. Es gibt zwar
>> keine Fehlermeldung, aber das Programm arbeitet auch nicht. Kann das
>> stimmen?
>
> Ja, weil die Strukturvariable lokal, aber nicht static ist. Dadurch
> geht ihr Inhalt zwischen zwei Interrupts möglicherweise verloren.
Das könnte der fehler gewesen sein. Ich habs wie folgt gehabt.
1
int main ( void ) {
2
  ...
3
}
4
ISR(TIM0_OVF_vect) {
5
  struct {
6
    static unsigned int counter:4;
7
    static signed int summand:2;
8
    static unsigned int oc_sel:1;
9
  } byte0;
10
  if ( byte0.summand == 0 ) byte0.summand = 1;
11
  ...
12
}
Ziemlich doof. Hat mir auch nicht gefallen und niht funktioniert.

von Karl H. (kbuchegg)


Lesenswert?

Hans Wurst wrote:

> Ich dachte die Zahl nach dem Doppelpunkt gibt die anzahl der Bytes an,
> die die Variable bekommt?

Spätestens an dieser Stelle ist der Hinweis auf Literatur angebracht. 
Eine Programmiersprache wie C, mit all ihren Feinheiten kann man nicht 
nach dem Prinzip "Versuch und Irrtum" lernen.

Die Zahl gibt die Bits an!

Alles andere wäre ja auch unsinng. Aus dem Datentyp int, char, long weiß 
der Compiler ja sowieso wieviele Bytes das haben soll.

von Leo B. (luigi)


Lesenswert?

>> Ich dachte die Zahl nach dem Doppelpunkt gibt die anzahl der Bytes an,
>> die die Variable bekommt?
> ...
> Die Zahl gibt die Bits an!
> ...

Ich meinte ja statt Byte Bit. Habs nur grad verdreht. Sorry.
Aber wo soll denn das char hin, dass alles in einem Byte bleibt?
vor jeder Variable macht ja keinen Sinn, dann sag ich ja ne variable mit 
8 bit die in einem bit gespeichert wird... irgendwie widersprüchlich.

von Karl H. (kbuchegg)


Lesenswert?

Ich sagte schon: LIteratur!

Der Compiler versucht dann alle Bitanforderungen in dem angegebenen 
Datentyp unterzubringen

struct Test
{
  unsigned char a : 4;
  unsigned char b : 4;
};


a und b teilen sich ein Byte (weil: unsigned char)
a bekommt davon 4 Bits und b bekommt 4 Bits

struct Test
{
  unsigned char a : 4;
  unsigned char b : 4;
  unsigned char c : 3;
};


Hier muss der Compiler 2 Bytes benutzen.
a und b passen gemeinsam in ein Byte und für C muss ein weiteres Byte 
angefangen werden, aus dem aber nur 3 Bits benutzt werden.

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.