Forum: Mikrocontroller und Digitale Elektronik Vektor int32_t mit 200 Stellen global


von Sven (Gast)


Lesenswert?

Hallo,

ich habe einen Vektor mit 200 Stellen mit dem Datentyp int32_t. Eine ISR 
greift auf diesen Vektor zu, d.h. ich muss diesen global definieren.
Das macht schon einmal 800byte SRAM. Das ist vermutlich für meinen 
Atmega8 zuviel.

Der Vektor wird zur Laufzeit des Programms verändert, d.h. Werte 
ausgelesen, verändert und wieder zurückgeschrieben.

Kann ich den Vektor auch in den PROGMEM auslagern? Welche Nachteile 
ergeben sich daraus? Zugriffszeit?

Danke.

LG,
Sven

von Karl H. (kbuchegg)


Lesenswert?

Sven schrieb:

> Kann ich den Vektor auch in den PROGMEM auslagern? Welche Nachteile
> ergeben sich daraus? Zugriffszeit?

Die Zugriffszeit wäre gar nicht mal so wild.
Der Hauptnachteil dürfte eher sein, dass man das PROGMEM, sprich das 
Flash nicht zur Laufzeit beschreiben kann :-)


800 Bytes sind zwar schon heftig viel, aber es bleiben noch 224 Bytes. 
Wenn man mit zu sehr damit umhaut, ist das immer noch eine Menge Holz.


Was steht den in den Daten? Kann man da mit cleverer Organisation 
eventuell noch was rausholen?

von Sven (Gast)


Lesenswert?

Die Daten sind im Prinzip eine Tabelle, die durch Benutzerinteraktion 
verändert werden kann (der Benutzer kann durch Konfigurationen die 
Tabelle im Prinzip neu befüllen während der Laufzeit). Dort sind dann 
Werte größer 16-bit hinterlegt.

Ich würde mit 16-bit vom Zahlenwert klarkommen (max. 65536), mein 
Problem ist, dass das ganze aber signed ist. Daher benötige ich noch 
1-bit für ein Vorzeichen. Vielleicht kann man hier mit cleverer 
Programmiertechnik tricksen und Zahl von Vorzeichen geschickt trennen 
und verwerten, aber dafür sind meine Kenntnisse zu wenig.

Der verbleibende SRAM ist aber ziemlich schnell aufgebraucht. Ich bin 
jetzt bei ~97% nach dem compilieren mit den 32-bit Array. Zuzüglich die 
dynamischen Variablen zur Laufzeit wird der Stack schnell dünn.

von spess53 (Gast)


Lesenswert?

Hi

Nimm einen pinkompatiblen ATMega328(p). Da hast du 2k RAM.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Sven schrieb:
> Die Daten sind im Prinzip eine Tabelle, die durch Benutzerinteraktion
> verändert werden kann (der Benutzer kann durch Konfigurationen die
> Tabelle im Prinzip neu befüllen während der Laufzeit). Dort sind dann
> Werte größer 16-bit hinterlegt.

Du hast mich missverstanden.
Was sind das für Werte? Welche Bedeutung haben sie?

> Ich würde mit 16-bit vom Zahlenwert klarkommen (max. 65536), mein
> Problem ist, dass das ganze aber signed ist. Daher benötige ich noch
> 1-bit für ein Vorzeichen. Vielleicht kann man hier mit cleverer
> Programmiertechnik tricksen

Na ja.
Für eine

struct MyValue
{
  uint8_t Sign;
  uint16_t Value;
};

struct MyValue Values[200];

muss man nicht weiter tricksen. Und spart so schon mal auf einen Schlag 
200 Bytes gegenüber einem reinen uint32_t Array ein.

> aber dafür sind meine Kenntnisse zu wenig.
dann musst du daran was ändern. Wer ein Haus bauen will muss mehr als 
nur einen Hammer kennen.

von Mac (Gast)


Lesenswert?

Du nimmst zwei Arrays. Der eine enthält nur die Absolutwerte (besteht 
also aus 200x uint16_t). Der andere nur die Vorzeichenbits. Und weil man 
nur ein bit pro Eintrag braucht, nimmt man dafür einen Bitarray, der aus 
200/8 uint8_t besteht.  Das macht dann insgesamt 425 Byte.

Bitarrays tauchen hier immer wieder auf, du kannst z.B. mal hier gucken:

Beitrag "Bit Array in C"

oder schauen was die Suche zu "bitarray" bringt.

von Klaus Maus (Gast)


Lesenswert?

Hi,

Sven schrieb:
> Ich würde mit 16-bit vom Zahlenwert klarkommen (max. 65536), mein
> Problem ist, dass das ganze aber signed ist. Daher benötige ich noch
> 1-bit für ein Vorzeichen. Vielleicht kann man hier mit cleverer
> Programmiertechnik tricksen und Zahl von Vorzeichen geschickt trennen
> und verwerten, aber dafür sind meine Kenntnisse zu wenig.

Nur 'ne Idee:
1
typedef struct {
2
    uint16_t      value1;
3
    uint16_t      value2;
4
    uint16_t      value3;
5
    uint16_t      value4;
6
    uint16_t      value5;
7
    uint16_t      value6;
8
    uint16_t      value7;
9
    uint16_t      value8;
10
    unsigned char sign1:1;
11
    unsigned char sign2:1;
12
    unsigned char sign3:1;
13
    unsigned char sign4:1;
14
    unsigned char sign5:1;
15
    unsigned char sign6:1;
16
    unsigned char sign7:1;
17
    unsigned char sign8:1;
18
} val_t __attribute__ ((packed));
19
val_t array[25];
20
(..)
21
array[3].value5 = 65535; // Betrag
22
array[3].sign5  = 1;     // Vorzeichen

oder
1
typedef struct {
2
    uint16_t value[8];
3
    uint8_t  sign;
4
} vul_t __attribute__ ((packed));
5
vul_t urray[25];
6
(..)
7
urray[3].value[5] = 65535; // Betrag
8
urray[3].sign = (1 << 5);  // Vorzeichen

Kostet jeweils nur 450 Byte RAM, mußt aber entweder mit entsprechenden 
Indizes (oder #defines) oder mit Bitoperatoren arbyten. Vorteil der 
ersten Lösung: bei festen Werten einfacher zu lesen und zu schreiben, 
Vorteil der zweiten Lösung: einfacher zu iterieren.

HTH,
Klaus

PS: ungetestet.

von Ralf G. (ralg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Der Hauptnachteil dürfte eher sein, dass man das PROGMEM, sprich das
> Flash nicht zur Laufzeit beschreiben kann :-)

Wenn man ein Stück Assembler mit in's Programm aufnehmen will:
In der Assembler-Hilfe ist das gut beschrieben, wie man das macht. Geht 
dann zwar nur 'seitenweise', könnte aber RAM sparen. Atmega8: Page Size: 
32 words

von Sven (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> struct MyValue
> {
>   uint8_t Sign;
>   uint16_t Value;
> };
>
> struct MyValue Values[200];

Hallo,

ok, das habe ich soweit verstanden, wie man die Daten ablegen kann.
Etwas schwer tue ich mir aber mit der Verwertung.

Lege ich dann eine globale Variable an ein einfaches int32_t s32foo;
Darin speichere ich mir dann den Wert des structs MyValue, z.B. so (für 
Stelle0):
1
s32foo = Values[0].Sign<< 31;  // Sign = 0 vorzeichenlos, Sign = 1 vorzeichenbehaftet, höchstes Bit der 32-bit Variable schieben (=Vorzeichenbit);
2
s32foo |= Values[0].Value;  // noch mit den Bits des Wertes verodern
Umgekehrt:
1
if (s32foo&(1<<32)){  // Vorzeichenbit gesetzt
2
  Values[0].Sign=1;
3
else{
4
  Values[0].Sign=0;
5
}
6
7
s32foo |= S32foo&0xFFFF;  // obersten 16-bits ausmaskieren
8
Values[0].Value=(uint16_t) s32foo ;

Ist das so ok?

von Ralf G. (ralg)


Lesenswert?

Die 'Schiebeorgie' beim Lesen würde ich mir sparen. Ich glaube, ein Test 
auf das MSB reicht. So, wie du das beim Schreiben auch machst.

von Karl H. (kbuchegg)


Lesenswert?

Sven schrieb:
> Karl Heinz Buchegger schrieb:
>> struct MyValue
>> {
>>   uint8_t Sign;
>>   uint16_t Value;
>> };
>>
>> struct MyValue Values[200];
>
> Hallo,
>
> ok, das habe ich soweit verstanden, wie man die Daten ablegen kann.
> Etwas schwer tue ich mir aber mit der Verwertung.

Der Knackpunkt liegt darin, dass du ohne Cast nicht auskommen wirst. 
D.h. wenn du den uint16_t verwenden willst, musst du ihn ja erst mal 
wieder zu einem korrekten int32_t 'aufblasen'. Dabei muss aber der 
unsigned Wert zuerst auf 32 Bit hochgehoben werden und erst dann kann 
man mit der Zusatzinfo aus Sign entscheiden, ob das jetzt ein positiver 
Wert oder ein negativer Wert war.

Am besten überlässt man diese Details gleich mal einer Funktion :-) Dann 
braucht man sich an der eigentlichen verewndenden Stelle nicht mehr 
darum kümmern. Man ruft einfach die Funktion auf, übergibt ihr einen 
struct MyValue Wert und kriegt den fix&fertigen int32_t zurück. Ich 
verwende dazu die Konvention, dass ein Sign Wert von 0 positive Zahlen 
kennzeichnet, während alles andere in Sign einen negativen Wert 
darstellt. Theoretisch sollten da eigentlich nur die Werte 0 und 1 
vorkommen - aber man weiß ja nie, sicher ist sicher. Da nehme ich 
Anleihen bei C, welches ja auch die Konvention hat: 0 ist logisch FALSE, 
alles andere ist TRUE
1
int32_t toInt32( struct MyValue* wert )
2
{
3
  int32_t result = wert->Value;
4
  if( wert->Sign != 0 )
5
    result = -result;
6
  return result;
7
}

Damit kann ich zb mit den einzelnen Array-Elementen jetzt leicht 
Operationen durchführen. zb
1
   for( i = 0; i < 200; i++ )
2
   {
3
     int32_t j = toInt32( Values[i] );
4
5
     ...
6
     sprintf( buffer, "%ld", toInt32( Values[i] ) );
7
8
     ...
9
10
     k = toInt32( Values[i] ) / toInt32( Values[0] );
11
12
     ...

Wann immer ich den echten 32-Bit Wert benötige, benutze ich die 
Funktion, die mir den aus so einem Struktur-Objekt erzeugt. Ob ich den 
dann in einem int32_t speichere, ob ich damit rechne, ob ich ihn 
ausgebe, ... ist dann meine Sache, wie ich den Wert benutzen will.

Selbiges in die umgekehrte Richtung. Die Umwandlung eines int32_t in ein 
struct MyValue Objekt, ... das macht mir gleich mal wieder eine 
Funktion. Ist der Wert negativ, dann mach ich ihn positiv, womit er (mit 
einer Ausnahme!) in den uint16_t passt und das Vorzeichen vermerke ich 
mir getrennt
1
void toMyValue( struct MyValue* value, int32_t wert )
2
{
3
  if( wert < 0 )
4
  {
5
    value->Value = (uint16_t)-wert;
6
    value->Sign = 1;
7
  }
8
  else
9
  {
10
    value->Value = (uint16_t)wert;
11
    value->Sign = 0;
12
  }
13
}



> Ist das so ok?
Widersteh der Versuchung da mit Bitoperationen um dich zu schmeissen. 
Lass das den Compiler machen. Du schreibst erst mal dein Programm so, 
wie es am einfachsten zu verstehen ist. Erst dann, wenn eine Analyse 
ergeben hat, dass das zu langsam ist, dann überlegt man, wie man es 
beschleunigen könnte. Aber da dieses 200-er Array ja wohl hauptsächlich 
nur dazu dient, eine Datenmenge zwischenzuspeichern und damit keine 
großartig vielen Operationen gemacht werden, ist das (geschätzt) alles 
erst mal nicht zeitkritisch.

Erst macht man es richtig - dann macht man es schnell.

von Fabian O. (xfr)


Lesenswert?

Mach es lieber so:
1
int32_t get_value(uint8_t n)
2
{
3
  if (Values[n].Sign) {
4
    return -((int32_t) Values[n].Value);
5
  } else {
6
    return Values[n].Value;
7
  }
8
}
9
10
void set_value(uint8_t n, int32_t val)
11
{
12
  if (val < 0) {
13
    Values[n].Sign = 1;
14
    Values[n].Value = -val;
15
  } else {
16
    Values[n].Sign = 0;
17
    Values[n].Value = val;
18
  }  
19
}

Edit: Karl Heinz war schneller ...

von Fabian O. (xfr)


Lesenswert?

Oder wenn Du noch mehr Speicher sparen willst:
1
static uint16_t values[200];
2
static uint8_t  signs[25];
3
4
int32_t get_value(uint8_t n)
5
{
6
  uint8_t sign = signs[n / 8] & (1 << (n % 8));
7
  if (sign) {
8
    return -((int32_t) values[n]);
9
  } else {
10
    return values[n];
11
  }
12
}
13
14
void set_value(uint8_t n, int32_t val)
15
{
16
  if (val < 0) {
17
    signs[n / 8] |= (1 << (n % 8));
18
    values[n] = -val;
19
  } else {
20
    signs[n / 8] &= ~(1 << (n % 8));
21
    values[n] = val;
22
  }  
23
}

Performance kann man noch verbessern, wenn man statt dem Shift um einen 
dynamischen Wert eine Tabelle für die Bitmasken nutzt, aber das sei dem 
interessierten Leser überlassen ... ;-)

von Sven (Gast)


Lesenswert?

Danke, das war sehr hilfreich und verständlich.
Auf das erste Lesen verstehe ich den Code, muss ihn mir aber mal noch 
Schritt für Schritt nachvollziehen, um das zu verinnerlichen.

Merci nochmals.

von Sven (Gast)


Lesenswert?

Fabian O. schrieb:
> int32_t get_value(uint8_t n)
> {
>   if (Values[n].Sign) {
>     return -((int32_t) Values[n].Value);
>   } else {
>     return Values[n].Value;
>   }
> }
>
> void set_value(uint8_t n, int32_t val)
> {
>   if (val < 0) {
>     Values[n].Sign = 1;
>     Values[n].Value = -val;
>   } else {
>     Values[n].Sign = 0;
>     Values[n].Value = val;
>   }
> }

Ich muss hier nochmal nachlegen, da ich ein Problem habe.
Ist der Übergabewert val für
1
void set_value(uint8_t n, int32_t val)
nur 16-bit groß, z.B. -10000, dann erkennt die Funktion nicht, dass der 
Wert negativ ist, d.h. es wird der else-Zweig verwendet.
Ändere ich die Funktion in
1
void set_value(uint8_t n, int16_t val)
funktioniert es. Ist aber nicht Sinn der Sache, da val je nachdem 32-bit 
oder 16-bit groß sein kann, d.h. ich brauche in jedem Fall 32-bit.

Stellt sich da der Compiler doof an oder wie kann ich das lösen?

Danke.

von Fabian O. (xfr)


Lesenswert?

Zeig mal, wie Du die Funktion aufrufst, möglichst den ganzen Code. 
Könnte z.B. sein, dass Du die -10000 in einer uint16_t- statt 
int16_t-Variable liegen hast.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wozu soll denn diese ganze Negiererei gut sein?

Man kann doch einfach
1
#include <stdint.h>
2
3
int32_t compose (uint16_t val, uint8_t sign)
4
{
5
    if (sign)
6
        return (int16_t) val;
7
    else
8
        return val;
9
}
10
11
uint8_t get_sign (int32_t val)
12
{
13
    return val < 0;
14
}
15
16
uint16_t get_val (int32_t val)
17
{
18
    return (uint16_t) val;
19
}

oder das beliebig direkt im Code nutzen.

von Bronco (Gast)


Lesenswert?

Johann L. schrieb:
> int32_t compose (uint16_t val, uint8_t sign)
> {
>     if (sign)
>         return (int16_t) val;
>     else
>         return val;
> }

Hä?
Mit
1
return (int16_t) val;
könnte er doch nicht einen gewünschten Wertebereich von -65535 bis 
+65535 abdecken, oder steh ich gerad auf dem Schlauch?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Bronco schrieb:
> Johann L. schrieb:
>> int32_t compose (uint16_t val, uint8_t sign)
>> {
>>     if (sign)
>>         return (int16_t) val;
>>     else
>>         return val;
>> }
>
> Hä?
> Mit
>
1
> return (int16_t) val;
2
>
> könnte er doch nicht einen gewünschten Wertebereich von -65535 bis
> +65535 abdecken, oder steh ich gerad auf dem Schlauch?

Nö, aber ich stand drauf :-)

Bitte reduzieren Sie die Anzahl der Zitatzeilen. Bitte reduzieren Sie 
die Anzahl der Zitatzeilen.

von Fabian O. (xfr)


Lesenswert?

Die Idee, die unteren zwei Bytes nicht zu ändern, hat aber schon was.

Mit
1
return (int32_t) ((uint32_t) val | 0xFFFF0000);
oder
1
return (int32_t) ((uint32_t) val - 65536);
sollte bei 2er-Komplement das Richtige rauskommen. Allerdings ist es 
streng genommen Type Punning, weil der Cast von unsigned nach signed 
"implementation defined" ist, wenn ich das richtig in Erinnerung habe. 
Oder gibt es eine standardkonforme Methode?

Außerdem muss der Compiler das kürzer als die Negation umsetzen, damit 
es was bringt ... Sofern man das nicht extrem häufig braucht, würde ich 
lieber die verständlichere und standardkonforme Version nehmen.

von Sven (Gast)


Angehängte Dateien:

Lesenswert?

So endlich von der Arbeit zu Hause.
Ich habe das Programm mal auf das wesentliche gekürzt, compiliert und 
das ausgegeben auf der UART, was nicht funktioniert.

Anbei das Programm und die UART-Mitschnitt.
Wie man sieht, wird bei der UART-Ausgabe bei Vorzeichen 0 gesetzt, d.h. 
er erkennt nicht, dass der Wert negativ ist, obwohl er es ist, siehe 
Ausgabe davor "Berechneter Wert".

Die Werte sind nur zu Demo, es können auch Werte theoretisch vorkommen, 
die größer als ein int16_t sind (ist hier nun nicht der Fall).

von Vlad T. (vlad_tepesch)


Lesenswert?

eine andere möglichkeit den Speicher zu sparen, wäre, sich zu überlegen, 
ob der Benutzer denn tatsächlich eine 1-bit Auflösung der 
Konfigurationsparameter braucht, oder ob es nicht reicht, wenn er nur in 
2er, 5er oder 10er Schritten oder einer logarithmischen Skala Werte 
eingeben kann.
In diesem Fall sucht man sich eine passende Bijektivität in den zur 
Speicherung zur Verfügung stehenden Wertebereich und speichert nur 
diesen Wert.

von Fabian O. (xfr)


Lesenswert?

Dein Code geht schon ganz am Anfang schief:
1
Wert_berechnet = pgm_read_word (&TabellePROGMEM[0][n]);  // Wert aus Flash lesen

Richtig wäre:
1
Wert_berechnet = (int16_t) pgm_read_word (&TabellePROGMEM[0][n]);  // Wert aus Flash lesen

von Sven (Gast)


Lesenswert?

Danke Fabian, damit geht es. Habe die Zeile geändert und nun läuft's.
Verstehen tu ich es aber noch nicht ganz.
Gut, der ausgelesene Wert ist ein int16_t. Aber ein int16_t passt doch 
in ein int32_t rein, wieso muss ich hier nochmals explizit casten?

von Fabian O. (xfr)


Lesenswert?

Sven schrieb:
> Gut, der ausgelesene Wert ist ein int16_t.

Eben nicht, es ist ein uint16_t. Wenn Du den in einen int32_t packst, 
entsteht kein negatives Vorzeichen. Deshalb der explizite Cast von einem 
uint16_t in einen int16_t und dann erst in int32_t.

Solche Probleme lassen sich übrigens gut vermeiden, wenn man mit 
passenden (lokalen) Variablentypen arbeitet, die auch nicht volatile 
sein müssen. Und für Zeilenumbrüche besser "\r\n" nehmen:
1
int main(void)
2
{
3
  char buffer [15];
4
5
  init_ports();
6
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
7
  
8
  for (uint8_t n=20; n<200; n++) {
9
    int16_t Wert_flash = pgm_read_word (&TabellePROGMEM[0][n]);  // Wert aus Flash lesen
10
  
11
    itoa( Wert_flash, buffer, 10 );
12
    uart_puts_P("Ausgelesener Wert aus PROGMEM:");
13
    uart_puts(buffer);
14
    uart_puts_P("\r\n");
15
  
16
    // Werte berechnen
17
    int32_t Wert_berechnet = (8000L*Wert_flash)/((n+1)*3);    // Berechne Winkelwerte in Timerzieten nach Vorschrift um
18
    ltoa( Wert_berechnet, buffer, 10 );
19
    uart_puts_P("Berechneter Wert:");
20
    uart_puts(buffer);
21
    uart_puts_P("\r\n");
22
  
23
    // Wandle Wert um in struct
24
    set_value(n, Wert_berechnet);
25
  
26
    // Gebe davon das Vorzeichen aus
27
    utoa( Tabellenwert[n].Vorzeichen, buffer, 10 );
28
    uart_puts_P("Vorzeichen:");
29
    uart_puts(buffer);
30
    uart_puts_P("\r\n");
31
  
32
    // Gebe dazu noch den Wert aus
33
    utoa( Tabellenwert[n].Wert, buffer, 10 );
34
    uart_puts_P("Wert gewandlet:");
35
    uart_puts(buffer);
36
    uart_puts_P("\r\n");
37
  
38
    // Wandle testweise zurück aus dem struct in ein int32_t und gebe es aus
39
    Wert_berechnet = get_value(n);
40
    set_value(n, Wert_berechnet);
41
    ltoa( Wert_berechnet, buffer, 10 );
42
    uart_puts_P("Wert rückgewandelt:");
43
    uart_puts(buffer);
44
    uart_puts_P("\r\n");
45
  }
46
47
  while (1) {
48
  } 
49
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fabian O. schrieb:
> Und für Zeilenumbrüche besser "\r\n" nehmen:

Aber nicht so:

>     uart_puts_P("\r\n");

Ein Zeilenumbruch in C ist ein \n.


Da hier keine OS-Unterstützung vorhanden ist, gibt man den \r zentral 
und an einer einzigen Stelle aus, etwa innerhalb der innersten 
uart_put, welche die Zeichen ausgibt.

Ob wirklich ein \r auszugeben ist oder nicht, hängt vom Filesystem des 
Host-OS a, hier also davon, ob zu einem Linux- oder MS-Win Terminal 
übertragen / angezeigt wird.

Und das will man nicht 100 mal im Cose stehen haben sondern an einer 
zentralen Stelle, etwa
1
void uart_putc (char c)
2
{
3
#ifdef HOST_WINDOWS
4
    if (c == '\n')
5
       uart_putc ('\r');
6
#endif /* HOST_WINDOWS */
7
8
    ...
9
}

Und compilieren mit -DHOST_WINDOWS

von Sven (Gast)


Lesenswert?

Fabian O. schrieb:
> Eben nicht, es ist ein uint16_t.

Hmm, wieso das? TabellePROGMEM ist doch vom Typ int16_t?

Diese Struktur werde ich mir angewöhnen, dass man alles schön der Reihe 
nach von Typ nach Typ macht, das ist sicherlich deutlich weniger 
fehleranfällig. Danke.

von Vlad T. (vlad_tepesch)


Lesenswert?

Na so ein Quatsch.
Dann muss ich ja je nachdem ob meine Gegenstelle ein unixoid, macoid 
oder windowsoid ist, ja eigene Firmwares und Geräteversionen bauen.

Damit sollte imho die Gegenstelle klar kommen.
Die meisten Terminal-Programme die ich kenne, lassen sich einstellen, 
bei was sie einen Umbruch machen und was sie bei einem Enter 
rauschicken.

von Fabian O. (xfr)


Lesenswert?

Sven schrieb:
> Hmm, wieso das? TabellePROGMEM ist doch vom Typ int16_t?

Ah, jetzt versteh ich Dein Problem. Die Sache ist, dass die Funktion 
pgm_read_word() nicht weiß, welchen Typ die Daten im Flash haben. Sie 
liest einfach von der Adresse, die Du ihr übergibst, 16 Bit und gibt sie 
als uint16_t zurück. Wenn Du die 16 Bit vorzeichenbehaftet 
interpretieren willst, musst Du sie explizit in ein int16_t speichern 
bzw. casten.

Ob Du TabellePROGMEM als int16_t oder uint16_t angelegt hast, spielt 
also so gesehen keine Rolle. Genau wegen solcher Dinge ist diese 
PROGMEM-Geschichte eine ziemliche Krücke ...

In neueren avr-gcc-Versionen (ab 4.7) gibt es zu PROGMEM die Alternative 
__flash. Dann braucht man keine extra Funktionen mehr, um aus dem Flash 
zu lesen. Beim aktuellen Atmel Studio ist leider Version 4.6 dabei, 
damit geht es noch nicht.

von Sven (Gast)


Lesenswert?

Ok, jetzt verstehe ich auch.
Weitergedacht wäre es folglich ein Problem, wenn man gemischte Werte im 
Flash ablegt (signed und unsigned) willkürlich verteilt im Vektor (also 
Vorzeichen der jeweiligen Stelle unbekannt bzw. ohne Algorithmus). Diese 
könnte ich dann doch niemals Vorzeichenrichtig holen mit der Funktion 
pgm_read_wort, oder?

Das ist jetzt bei mir zwar nicht der Fall, möchte aber nur 
weiterdenken...

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Hallo,

ich hab ein bisschen mitgelesen und mich über viele wertvolle Beiträge 
gefreut. Eine Frage wurde aber noch nicht beantwortet, und auf diese 
Antwort wär ich schon irgendwie neugierig, weil sie vielleicht deutliche 
einfachere Lösungen ermöglicht:

Karl Heinz Buchegger schrieb:
> Was sind das für Werte? Welche Bedeutung haben sie?

Weitere Fragen von mir dazu:

Wird wirklich ein Wertebereich von -65536..+65535 benötigt, oder reicht 
zum Beispiel -16384..49151 ?

Ist wirklich eine Genauigkeit von +/-1 notwendig, oder reicht es, wenn 
der fragliche Zahlenwert vor dem Speichern auf 2er-Schritte gerundet 
wird?

von Sven (Gast)


Lesenswert?

Die Werte stellen eine Tabelle dar, aus der sich je nach Bedarf ein 
Timer-Wert geholt wird bzw. der Wert in der Tabelle mit dem aktuellen 
Timer-Wert verrechnet wird und dann ein Compare Match vorlädt.

Positiv werden die Werte in der Regel nur bis ca. 5000. Negativ benötige 
ich aber bis -60000, also 16-bit Breite könnte gehen, wenn es darum 
geht.

Eine Genauigkeit für den Compare Matach von +/-2 ist auch vertretbar.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sven schrieb:

> Positiv werden die Werte in der Regel nur bis ca. 5000. Negativ benötige
> ich aber bis -60000, also 16-bit Breite könnte gehen, wenn es darum
> geht.

Das passt doch prima. Einfach nen Offset von 60000 drauf und fertig.

Und 200 Timer???

von Sebastian S. (sebastians)


Lesenswert?

Je nach Compiler-Version kannst du die 24 bit - Zahlen vielleicht auch 
mit ganz wenig Aufwand bekommen:
http://gcc.gnu.org/wiki/avr-gcc#Types

von Fabian O. (xfr)


Lesenswert?

Sven schrieb:
> Weitergedacht wäre es folglich ein Problem, wenn man gemischte Werte im
> Flash ablegt (signed und unsigned) willkürlich verteilt im Vektor (also
> Vorzeichen der jeweiligen Stelle unbekannt bzw. ohne Algorithmus). Diese
> könnte ich dann doch niemals Vorzeichenrichtig holen mit der Funktion
> pgm_read_wort, oder?

In so einem Fall bietet es sich an, die Daten in einer Struktur 
abzulegen und mit memcpy_P() zu holen.

Beispiel:
1
typedef struct {
2
  uint8_t  a;
3
  int8_t   b;
4
  uint16_t c;
5
  int16_t  d;
6
} mischmasch_t;
7
8
static const PROGMEM mischmasch_t mischmasch_array[] = {
9
  { 1,  -1,  1234, -4321},
10
  {13,  42, 42000, -1337},
11
  // ...
12
};
13
14
#define MISCHMASCH_NUM (sizeof(mischmasch_array) / sizeof(*mischmasch_array))
15
16
void zeige_werte(void)
17
{
18
  for (uint8_t i = 0; i < MISCHMASCH_NUM; i++) {
19
    mischmasch_t m;
20
    memcpy_P(&m, mischmasch_array + i, sizeof(*mischmasch_array));
21
    printf_P(PSTR("i = %u, a = %u, b = %i, c = %u, d = %i\n"), i, m.a, m.b, m.c, m.d);
22
  }
23
}

von Fabian O. (xfr)


Lesenswert?

Sven schrieb:
> Die Werte stellen eine Tabelle dar, aus der sich je nach Bedarf ein
> Timer-Wert geholt wird bzw. der Wert in der Tabelle mit dem aktuellen
> Timer-Wert verrechnet wird und dann ein Compare Match vorlädt.
>
> Positiv werden die Werte in der Regel nur bis ca. 5000. Negativ benötige
> ich aber bis -60000, also 16-bit Breite könnte gehen, wenn es darum
> geht.
>
> Eine Genauigkeit für den Compare Matach von +/-2 ist auch vertretbar.

Das hättest Du wirklich einfacher haben können ... Entweder wie gesagt 
mit einem festen Offset oder die halbierten Werte als int16_t speichern.

von Sven (Gast)


Lesenswert?

Das ist natürlich wahr. Manchmal denkt man einfach zu kompliziert.

von Fabian O. (xfr)


Lesenswert?

Hab nochmal den Code durchgelesen und noch eine kleine 
Schönheitskorrektur. Statt
1
mischmasch_t m;
2
memcpy_P(&m, mischmasch_array + i, sizeof(*mischmasch_array));
ist es so hübscher:
1
mischmasch_t m;
2
memcpy_P(&m, mischmasch_array + i, sizeof(mischmasch_t));

Nur der Vollständigkeit halber, falls jemand das Beispiel als Vorlage 
für irgendwas nehmen sollte ...

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Sven schrieb:
> Die Werte stellen eine Tabelle dar, aus der sich je nach Bedarf ein
> Timer-Wert geholt wird bzw. der Wert in der Tabelle mit dem aktuellen
> Timer-Wert verrechnet wird und dann ein Compare Match vorlädt.
>
> Positiv werden die Werte in der Regel nur bis ca. 5000. Negativ benötige
> ich aber bis -60000, also 16-bit Breite könnte gehen, wenn es darum
> geht.
>
> Eine Genauigkeit für den Compare Matach von +/-2 ist auch vertretbar.

Puh - gut, das ich nochmal nachgehakt habe. :-)

Dann ist die Lösung wirklich einfach: Du verwendest uint16_t in der 
Tabelle. Vor dem Schreiben in den Speicher addierst du 60000 zum Wert, 
nach dem Lesen aus dem Speicher schiebst du den Wert in eine 
32-Bit-Variable (signed) und subtrahierst 60000 – genau wie Johann 
vorschlägt.

Eine andere, ebenso einfache Lösung wäre es, die Zahlen generell als 
halben Wert als int16_t zu verarbeiten und zu speichern (signed).

Trotzdem – falls mal jemand ein ähnliches Problem hat, das sich nicht so 
einfach lösen lässt wie bei Sven, finde ich die vielen Vorschläge in 
diesem Thread sehr interessant. Eure Arbeit war also bestimmt nicht "für 
die Katz"! :-)

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.