Forum: Compiler & IDEs externe Konstante im Flash


von Ralf (Gast)


Lesenswert?

Mahlzeit!
Ich hätte da gern mal ein Problem ;-)

Datei 'Const.c':
1
#include <avr/pgmspace.h>
2
3
const uint8_t K_AdresseZusatz PROGMEM = 48;
Datei 'Test.c':
1
#include <avr/io.h>
2
#include <avr/pgmspace.h>
3
4
uint8_t AdressePlatine;
5
extern const uint8_t K_AdresseZusatz PROGMEM;
6
7
// const uint8_t K_AdresseZusatz PROGMEM = 48;  
8
// => so funktioniert's (ohne 'Const.c')
9
// => Liegt die Konstante (oder auch Variable) im RAM funktioniert's auch
10
11
int main()
12
{
13
 AdressePlatine=(((~PINB) & 0b11100000) >> 5) | K_AdresseZusatz;
14
}
Bei dieser Konstellation zeigt der AVR-Studio-Simulator mir im 
Überwachungsfenster zwar für die Konstante 'K_AdresseZusatz' den 
korrekten Wert an, hat aber keine Lust damit zu rechnen!?
Der ASM-Code dazu ist soweit*) korrekt, also gehe ich davon aus, dass es 
im Controller funktioniert(?):
1
 AdressePlatine=(((~PINB) & 0b11100000) >> 5) | K_AdresseZusatz;
2
  4a:  86 b3         in  r24, 0x16  ; 22
3
  4c:  90 e0         ldi  r25, 0x00  ; 0
4
  4e:  80 95         com  r24
5
  50:  90 95         com  r25
6
  52:  80 7e         andi  r24, 0xE0  ; 224
7
  54:  90 70         andi  r25, 0x00  ; 0
8
  56:  25 e0         ldi  r18, 0x05  ; 5
9
  58:  95 95         asr  r25
10
  5a:  87 95         ror  r24
11
  5c:  2a 95         dec  r18
12
  5e:  e1 f7         brne  .-8        ; 0x58 <main+0xe>
13
  60:  20 91 26 00   lds  r18, 0x0026
14
  64:  28 2b         or  r18, r24
15
  66:  20 93 60 00   sts  0x0060, r18
*) Ich habe schon mit Casts um mich geschmissen, konnte aber dem 
Compiler nicht abgewöhnen, die Operationen (Bitverknüpfungen, Schieben) 
in 16Bit zu machen. Geht das irgendwie? Außerdem: wieso 'asr r25', wenn 
alles 'uint8_t' ist?

Bitte Vorschläge, danke!

von (prx) A. K. (prx)


Lesenswert?

Auf PROGMEM Daten kann man nicht direkt zugreifen.
Doku lesen => pgm_read_xxx().

von Ralf (Gast)


Lesenswert?

A. K. schrieb:
> Auf PROGMEM Daten kann man nicht direkt zugreifen. Doku lesen.
???

Ralf schrieb:
> // const uint8_t K_AdresseZusatz PROGMEM = 48;
> // => so funktioniert's (ohne 'Const.c')

Und der erzeugte Assembler-Code ist ja korrekt. So wie man's auch direkt 
macht:

Ralf schrieb:
> 60:  20 91 26 00   lds  r18, 0x0026
> 64:  28 2b         or  r18, r24
> 66:  20 93 60 00   sts  0x0060, r18

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> Und der erzeugte Assembler-Code ist ja korrekt.

Nicht wirklich. PROGMEM Daten liegen im Flash und das ist 
ausschliesslich per (E)LPM erreichbar. Kein LPM => keine korrekten 
Daten.

von Hans (Gast)


Lesenswert?

Auf PROGMEM Daten kann man nicht direkt zugreifen.
Doku lesen => pgm_read_xxx().

von Ralf (Gast)


Lesenswert?

A. K. schrieb:
> Kein LPM => keine korrekten Daten.
Genau. Ist mir gerade auch aufgefallen. Das hab ich völlig übersehen. 
Die Konstante wird gar nicht berücksichtigt im ASM-Code :-(
Warum dann aber Variante 2 (nicht 'extern') funktioniert, ist mir jetzt 
auch klar: die Konstante wird nicht im Flash gespeichert, sondern gleich 
an der entsprechenden Stelle in den Code gebastelt. Muss ich mal 
'pgm_read' probieren (hoffe das braucht nicht zuviel Code!)

Bleibt noch: Warum 16Bit?

von Ralf (Gast)


Lesenswert?

1
AdressePlatine=(((~PINB) & 0b11100000) >> 5) | pgm_read_byte(&K_AdresseZusatz);
Hätt' ich auch selber drauf kommen können. Naja, taste mich noch ran an 
GCC.

von Ralf (Gast)


Lesenswert?

Ralf schrieb:
> konnte aber dem
> Compiler nicht abgewöhnen, die Operationen (Bitverknüpfungen, Schieben)
> in 16Bit zu machen. Geht das irgendwie? Außerdem: wieso 'asr r25', wenn
> alles 'uint8_t' ist?
Ich konnt's ihm abgewöhnen :-)
1
 AdressePlatine=(((~PINB) & 0b11100000));
2
 AdressePlatine = AdressePlatine >> 5;
3
 AdressePlatine |= pgm_read_byte(&K_AdresseZusatz);
Da gibt der Compiler sich richtig Mühe!

Trotzdem: warum erst 16Bit und 'asr r25'?

von Rolf Magnus (Gast)


Lesenswert?

Ralf schrieb:
> Trotzdem: warum erst 16Bit und 'asr r25'?

Weil die C-Definition vorschreibt, daß bei einer Rechenoperation alle 
Operanden, die kleiner als int sind, erstmal zu int erweitert werden und 
die Rechnung dann in int durchgeführt wird. Der Compiler darf diese 
Erweiterung wegoptimieren, aber nur, wenn er garantieren kann, daß in 
jedem Fall das Ergebnis dadurch nicht beeinträchtigt wird. Jetzt ist bei 
AVR aber die native Wortgröße kleiner als int, und da dieser Fall in GCC 
eine Ausnahme darstellt, sind solche Optimierungen da nicht so 
umfassend.

von Karl H. (kbuchegg)


Lesenswert?

Ralf schrieb:
> Ralf schrieb:
>> konnte aber dem
>> Compiler nicht abgewöhnen, die Operationen (Bitverknüpfungen, Schieben)
>> in 16Bit zu machen. Geht das irgendwie? Außerdem: wieso 'asr r25', wenn
>> alles 'uint8_t' ist?
> Ich konnt's ihm abgewöhnen :-)
>
1
>  AdressePlatine=(((~PINB) & 0b11100000));
2
>  AdressePlatine = AdressePlatine >> 5;
3
>  AdressePlatine |= pgm_read_byte(&K_AdresseZusatz);
4
>
> Da gibt der Compiler sich richtig Mühe!
>
> Trotzdem: warum erst 16Bit und 'asr r25'?

Probier mal
1
uint8_t pin = ~PIND;
2
AdressePlatine=(( pin & 0b11100000) >> 5) | pgm_read_byte(&K_AdresseZusatz);

durch das ~ zwingst du den Compiler, den Wert auf 16 Bit aufzublasen.

von Ralf (Gast)


Lesenswert?

@Rolf Magnus
Sozusagen: Er könnte, wenn er wollte ;-)

@Karl Heinz Buchegger
Ich hatte mir extra den ASM-Code angeschaut, mit meinen Kenntnissen 
könnte ich da nichts verbessern (in Assembler hätte ich das genauso 
gemacht, außer: r30 gleich als Ziel für lpm zu verwenden, hätte ich mich 
nicht 'getraut'):
1
 AdressePlatine=(((~PINB) & 0b11100000));
2
  4a:  86 b3         in  r24, 0x16  ; 22
3
 AdressePlatine = AdressePlatine >> 5;
4
 AdressePlatine |= pgm_read_byte(&K_AdresseZusatz);
5
  4c:  e6 e2         ldi  r30, 0x26  ; 38
6
  4e:  f0 e0         ldi  r31, 0x00  ; 0
7
  50:  e4 91         lpm  r30, Z+
8
  52:  80 95         com  r24
9
  54:  82 95         swap  r24
10
  56:  86 95         lsr  r24
11
  58:  87 70         andi  r24, 0x07  ; 7
12
  5a:  e8 2b         or  r30, r24
13
  5c:  e0 93 60 00   sts  0x0060, r30
('swap + lsr' fand ich gut, dass er das im Sortiment hat :-) Bei der 
'16Bit-Variante' war das ja noch eine Schleife!)

von Ralf (Gast)


Lesenswert?

@Karl Heinz Buchegger
Hatte gestern keine Gelegenheit mehr das zu probieren. Mit deinem 
Zweizeiler ist das ganze wieder etwas übersichtlicher.
-----
Karl Heinz Buchegger schrieb:
> durch das ~ zwingst du den Compiler, den Wert auf 16 Bit aufzublasen.

Rolf Magnus schrieb:
> Weil die C-Definition vorschreibt, daß bei einer Rechenoperation alle
> Operanden, die kleiner als int sind, erstmal zu int erweitert werden und
> die Rechnung dann in int durchgeführt wird.

Kann man sowas als Laie wissen? Dann habe ich das immer überlesen. 
Vielleicht, weil es auf dem PC nicht so wichtig ist...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ralf schrieb:

> int main()
> {
>  AdressePlatine=(((~PINB) & 0b11100000) >> 5) | K_AdresseZusatz;
> }
> [/c]
> [...]
> *) Ich habe schon mit Casts um mich geschmissen, konnte aber dem
> Compiler nicht abgewöhnen, die Operationen (Bitverknüpfungen, Schieben)
> in 16Bit zu machen. Geht das irgendwie?

Der Cast muss an der richtigen Stelle sein:
1
AdressePlatine = ((((uint8_t) ~PINB) & 0b11100000) >> 5) | K_AdresseZusatz;

> Kann man sowas als Laie wissen?

Naja, eine Sprachfestlegung nimmt keine Rücksicht darauf, ob man Laie 
ist oder nicht.

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> Kann man sowas als Laie wissen?

In den Büchern über C steht das üblicherweise drin. Aber mehr es dort 
reinschreiben geht nicht, lesen muss der Adept es dann schon selber.

von Ralf (Gast)


Lesenswert?

Johann L. schrieb:
> Der Cast muss an der richtigen Stelle sein:
> AdressePlatine = ((((uint8_t) ~PINB) & 0b11100000) >> 5) | K_AdresseZusatz;

Ich glaube, das war die einzige Stelle, die ich nicht probiert habe. So 
ein Port hat ja nun sowas von 8Bit...
Aber: 'Wie wir jetzt wissen' ;-)
Karl Heinz Buchegger schrieb:
> durch das ~ zwingst du den Compiler, den Wert auf 16 Bit aufzublasen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ralf schrieb:
> Johann L. schrieb:
>> Der Cast muss an der richtigen Stelle sein:
>> AdressePlatine = ((((uint8_t) ~PINB) & 0b11100000) >> 5) | K_AdresseZusatz;
>
> Ich glaube, das war die einzige Stelle, die ich nicht probiert habe.

Freilich geht aus sowas:
1
void foo2()
2
{
3
    AdressePlatine = (PINB >> 5) ^ K_AdresseZusatz;
4
}

indem du in K_AdresseZusatz die unteren 3 Bits setzt. Ist ja eh ne 
Konstante, und da tut das nicht weh :-)

von Ralf (Gast)


Lesenswert?

Johann L. schrieb:
> Freilich geht aus sowas:void foo2()
> {
>     AdressePlatine = (PINB >> 5) ^ K_AdresseZusatz;
> }
> indem du in K_AdresseZusatz die unteren 3 Bits setzt. Ist ja eh ne
> Konstante, und da tut das nicht weh :-)
>
Kann ich nachvollziehen :-)
Das ist natürlich mal was. Wäre ich nie drauf gekommen.

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.