Forum: Compiler & IDEs Wie bekommt man eine double Variable in ein char Array?


von Steffen H. (avrsteffen)


Lesenswert?

Hallo,

Ich schon wieder. Wie oben schon beschrieben bekomme ich es nicht hin in 
ein char Array eine double/float Variable hineinzubringen.

Hier mal der Versuch:
1
static char spi_buf[32];
2
3
void sendDouble(double n )
4
{
5
   spi_buf[0] = n>>24;
6
   spi_buf[1] = n>>16;
7
   spi_buf[2] = n>>8;
8
   spi_buf[3] = n;
9
}

Was mach ich da falsch? Hat das was mit casten zu tun?

Steffen

von Andreas B. (andreasb)


Lesenswert?

Steffen H. schrieb:
1
 static char spi_buf[32];
2
 
3
 void sendDouble(double n )
4
 {
5
    memcpy(spi_buf, &n, sizeof(double));
6
 }

So sollte es gehen...

Ansonsten dein n mal auf
char * ptr = (char*) &n;

casten, dann müsste auch
spi_buf[0] = ptr[3];

funktionieren, aber memcpy ist einfacher...


mfg Andreas

von Tom M. (tomm) Benutzerseite


Lesenswert?

Lass doch die libc die Arbeit machen:
1
#include <string.h>
2
3
...
4
5
memcpy(spi_buf, &n, sizeof(double));

Steffen H. schrieb:
> static char spi_buf[32];

Wozu 32 chars?

von Steffen H. (avrsteffen)


Lesenswert?

Danke für die Anworten.

Mit memcpy funktioniert das auch, aber ich habe vorher noch eine 
Berechnung und will dann die berechneten double/Floats hintereinander in 
das spi_buf Array schreiben. Deswegen der gescheiterte Klimmzug über 
die Funktion sendDouble. Also ich versuchs konkreter zu erklären durch 
mehr code.

Variablen:
1
static char spi_buf[32];
2
typedef struct {
3
  int32_t position[3];  // Real-time machine position vector in steps.  
4
  uint8_t coord_select; // Active work coordinate system number.
5
  double coord_offset[3]; // G92 coordinate offset (work coordinates) 
6
} system_t;
7
extern system_t sys;

Hier sollen die 3 Positionsdaten in den spi_buf nacheinander gespeichert 
werden, damit sie dann per SPI (interrupted) verschickt werden können.
1
void send_status_report()
2
{
3
double stepper_position[3];
4
  memcpy(stepper_position,sys.position,sizeof(sys.position));
5
6
  sendDouble(stepper_position[X_AXIS])/(settings.steps_per_mm[X_AXIS]));
7
  sendDouble(stepper_position[Y_AXIS])/(settings.steps_per_mm[Y_AXIS]));
8
  sendDouble(stepper_position[Z_AXIS])/(settings.steps_per_mm[Z_AXIS]));
9
}

Hier muss dann natürlich noch der Zähler für die Position im Array mit 
der Funktion mitgeliefert und von der aufrufenden übergeben werden.
1
void sendDouble(double n )
2
{
3
   spi_buf[0] = n>>24;
4
   spi_buf[1] = n>>16;
5
   spi_buf[2] = n>>8;
6
   spi_buf[3] = n;
7
}
allerdings klappt es so nicht.

Tom M. schrieb:
> Wozu 32 chars?

Ist der maximal zu erwartende spi transfer.


Steffen

von Andreas B. (andreasb)


Lesenswert?

Steffen H. schrieb:
> Ist der maximal zu erwartende spi transfer.
>
>
> Steffen

memcpy(spi_buf+pos*sizeof(double), &n, sizeof(double));

Wobei pos 0 ... sizeof(spi_buf) / sizeof(double).

Vergiss das Shiften, ist so einfacher, natürlich musst du pos 
entsprechend hochzählen bei jedem Senden.


mfg Andreas

von Steffen H. (avrsteffen)


Lesenswert?

Andreas B. schrieb:
> Wobei pos 0 ... sizeof(spi_buf) / sizeof(double).
>
> Vergiss das Shiften, ist so einfacher, natürlich musst du pos
> entsprechend hochzählen bei jedem Senden.

Heißt das ich nach jedem Aufruf
1
sendDouble(stepper_position[X_AXIS])/(settings.steps_per_mm[X_AXIS]));
irgendwie Pos hochzählen muss? So etwa?
1
uint8_t pos=0;
2
3
sendDouble(stepper_position[X_AXIS])/(settings.steps_per_mm[X_AXIS]),pos);
4
pos += sizeof(double);
5
sendDouble(stepper_position[Y_AXIS])/(settings.steps_per_mm[X_AXIS]),pos);
6
..

Und in der Funktion sendDouble dann:
1
void sendDouble(double n, uint8_t pos)
2
{
3
memcpy(spi_buf+pos*sizeof(double), &n, sizeof(double));
4
}
Sollte es so gehen?

Gruß Steffen

von Karl H. (kbuchegg)


Lesenswert?

Steffen H. schrieb:

sendDouble(stepper_position[Y_AXIS])/(settings.steps_per_mm[X_AXIS]),pos 
);
> ..
> [/c]
>
> Und in der Funktion sendDouble dann:
>
1
> void sendDouble(double n, uint8_t pos)
2
> {
3
> memcpy(spi_buf+pos*sizeof(double), &n, sizeof(double));
4
> }
5
>
> Sollte es so gehen?

Wenn du hier mit sizeof(double) multiplizierst, dann brauchst du pos 
aussen nicht mit sizeof(double) erhöhen. Du hast doch die Abstände schon 
in der Multiplikation berücksichtigt.

Wenn du jeweils 5 SChachteln in Folge (in einer Sequenz) aufstellst, wo 
steht dann die erste Schachtel der 6-ten Sequenz?
Die steht in der 6*5 = 30-ten Position,

sizeof liefert dir einfach nur die Größe des angegebenen in Bytes.

von Steffen H. (avrsteffen)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn du hier mit sizeof(double) multiplizierst, dann brauchst du pos
> aussen nicht mit sizeof(double) erhöhen. Du hast doch die Abstände schon
> in der Multiplikation berücksichtigt.

Stimmt, so gehts natürlich auch. Hab ich ja voll übersehen. Aber pos 
muss ich außen ja trotzdem erhöhen. Dann allerdings nur um 1. (pos++;)
Würde mich jetzt mal interessieren, welche der beiden Varianten hier 
mehr Resourcen verbraucht.

Ich sehe gerade, ich hab es jetzt eh etwas anders gelöst. Aber es 
funktioniert.
1
void send_status_report()
2
{
3
uint8_t pos=0;
4
double stepper_position[3];
5
  memcpy(stepper_position,sys.position,sizeof(sys.position));
6
7
   sendDouble((stepper_position[X_AXIS]/(settings.steps_per_mm[X_AXIS])), pos);
8
   pos += sizeof(double);
9
   sendDouble((stepper_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])), pos);
10
   pos += sizeof(double);
11
   sendDouble((stepper_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])), pos);
12
   spi_write_data(spi_buf,11,CS_EXT);
13
14
}
15
16
17
void sendDouble(double n, uint8_t i )
18
{
19
char * ptr = (char*) &n;
20
21
   spi_buf[i++] = ptr[3];
22
   spi_buf[i++] = ptr[2];
23
   spi_buf[i++] = ptr[1];
24
   spi_buf[i++] = ptr[0];
25
}
26
27
28
// write one or multiple bytes to the Serial SPI memory
29
void spi_write_data(char* source, char wr_bytes, char cs_adr)
30
{
31
   spi_busy = true;
32
   nb_byte = wr_bytes;
33
   byte_cnt = 0; 
34
   data_ptr = source; 
35
   set_spi_cs(cs_adr); // Pull down the chip select line of the SPI device
36
   SPDR = *data_ptr;   // send the first byte        
37
}
38
39
40
// Interrupt Routine Master Mode (interrupt controlled)
41
ISR(SPI_STC_vect)
42
{
43
 data_ptr++;   // point to the next byte (even if it was the last) 
44
   
45
   if (byte_cnt == nb_byte )  // is the last byte sent?
46
    {
47
    PORTB &= ~(CS_MASK);
48
    PORTB |= CS_HI;    // Pull high the chip select line of the SPI device
49
    spi_busy = false;  // return to the idle state
50
    }
51
   else
52
    {      
53
     byte_cnt ++;
54
     SPDR = *data_ptr;
55
    }
56
}

Dank an all den freundlichen Helfern.

Gruß Steffen

von Karl H. (kbuchegg)


Lesenswert?

Widersteh der Versuchung zu 'künsteln'. Die einfachste Variante ist die 
beste.
1
void send_status_report()
2
{
3
  double stepper_position[3];
4
5
  stepper_position[X_AXIS] = sys.position[X_AXIS] / settings.steps_per_mm[X_AXIS];
6
  stepper_position[Y_AXIS] = sys.position[Y_AXIS] / settings.steps_per_mm[Y_AXIS];
7
  stepper_position[Z_AXIS] = sys.position[Z_AXIS] / settings.steps_per_mm[Z_AXIS];
8
9
  memcpy( spi_buf, stepper_position, sizeof(stepper_position) );
10
11
  spi_write_data(spi_buf, 11, CS_EXT);
12
}


Bist du sicher, dass die 11 zum Schluss korrekt sind?
Wenn das eigentlich 12 sein sollten (wegen 3*sizeof(double) == 12), dann 
noch besser so
1
void send_status_report()
2
{
3
  double stepper_position[3];
4
5
  stepper_position[X_AXIS] = sys.position[X_AXIS] / settings.steps_per_mm[X_AXIS];
6
  stepper_position[Y_AXIS] = sys.position[Y_AXIS] / settings.steps_per_mm[Y_AXIS];
7
  stepper_position[Z_AXIS] = sys.position[Z_AXIS] / settings.steps_per_mm[Z_AXIS];
8
9
  spi_write_data( (unsigned char*)stepper_position, sizeof(stepper_position), CS_EXT);
10
}

von Steffen H. (avrsteffen)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Bist du sicher, dass die 11 zum Schluss korrekt sind?
> Wenn das eigentlich 12 sein sollten (wegen 3*sizeof(double) == 12
Ja, das ist schon richtig so. Denn in der spi_write_data wird ja schon 
1Byte an SPDR übergeben. Und somit fehlt eins zum countern in der ISR.

Danke für die Hilfe.

Steffen

von Karl H. (kbuchegg)


Lesenswert?

Steffen H. schrieb:
> Karl Heinz Buchegger schrieb:
>> Bist du sicher, dass die 11 zum Schluss korrekt sind?
>> Wenn das eigentlich 12 sein sollten (wegen 3*sizeof(double) == 12
> Ja, das ist schon richtig so. Denn in der spi_write_data wird ja schon
> 1Byte an SPDR übergeben. Und somit fehlt eins zum countern in der ISR.

ui.
Solche Sachen sollte die spi_write_data selber regeln.
Du übergibst 12 Bytes an die Funktion. Die Funktion stellt eines in SPDR 
und 11 in einen Buffer.

Das ist das, was man erwarten würde: Ich habe 12 Bytes und das sage ich 
auch genau so der Funktion. Wie die Funktion das dann intern regelt, das 
ist Sache der Funktion - das geht mich als Aufrufer nichts an. Ich weiß 
nur, dass ich 12 Bytes auf die Reise bringen will, und das teile ich 
auch der Funktion so mit.

Sowas sind typische Stolpersteine über die du in ein paar Wochen mehr 
als einmal stolpern wirst. Gestalte Funktionalitäten nach Möglichkeit 
immer so, dass sie FÜR DEN AUFRUFER(!) logisch sind. Denn der muss damit 
klar kommen und je weniger Spezial- und Sonderfälle man beim Aufruf 
einer Funktion wissen bzw. unterscheiden muss, desto weniger läuft man 
Gefahr dabei einen Fehler zu machen. Und Fehler macht man als 
Programmierer - du genauso wie ich.

von Oliver S. (oliverso)


Lesenswert?

Steffen H. schrieb:
> Was mach ich da falsch? Hat das was mit casten zu tun?

Um das auch noch mal aufzunehmen: Beantworte dir die Frage doch selber, 
zur Not auch mit Hilfe (d)eines C-Buches. Der Lerneffekt daraus wird 
dein Verständnis zu Datentypen in C erheblich erweitern.

Oliver

von Steffen H. (avrsteffen)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Solche Sachen sollte die spi_write_data selber regeln.
> Du übergibst 12 Bytes an die Funktion. Die Funktion stellt eines in SPDR
> und 11 in einen Buffer.

So hab ich es jetzt gemacht. War nur eine kleine Änderung in der 
spi_write_data .
1
  nb_byte = wr_bytes-1;

Steffen

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.