Komisches Verhalten von Pointer auf Struct

OP #2311377
Lesenswert?

Hallo!

Ich möchte verschiedene Befehle an ein GSM Modul schicken. Dafür habe 
ich ein Struct definiert, in dem alle Strings (Befehle) stehen.

Dies sieht so aus:
1
#define F_CPU 11059200UL
2
#define BAUD 115200UL
3
#define BAUD_VAL ((F_CPU + BAUD * 8) / (BAUD * 16) - 1)
4

5
#include <avr/io.h>
6
#include <util/delay.h>
7
#include <USART.c>
8
#include <LCD.c>
9

10
// define struct with all AT commands
11
typedef struct
12
{
13
  unsigned char AT[3];
14
  unsigned char ATE0[5];
15
  unsigned char AT_CPIN[13];
16
  unsigned char AT_COPS[10];
17
  unsigned char AT_CMGF[10];
18
  unsigned char AT_CNMI[18];
19
  unsigned char AT_CMGR[10];
20
  unsigned char AT_CMGS[24];
21
  unsigned char SMS_TEXT_ON[9];
22
  unsigned char SMS_TEXT_OFF[9];
23
} CHAR_ARRAY;
24

25
// initialise struct and define pointer
26
CHAR_ARRAY AT_CMD =
27
{
28
  "AT\0",
29
  "ATE0\0",
30
  "AT+CPIN=4053\0",
31
  "AT+COPS=?\0",
32
  "AT+CMGF=1\0",
33
  "AT+CNMI=1,1,0,2,1\0",
34
  "AT+CMGR=0\0",
35
  "AT+CMGS=\"+436641111111\"\0",
36
  "HZG EIN!\0",
37
  "HZG AUS!\0"
38
};
39

40
// create pointer to command struct
41
CHAR_ARRAY *AT_CMD_PTR = &AT_CMD;

Die Subroutine, die die Stings dann über USART an das GSM Modul schickt 
siehst so aus:
1
void GSM_WRITE (unsigned char *CMD)
2
{
3
  // clear display
4
  LCD_COMMAND(0x01);
5

6
  while (*CMD)
7
  {
8
    LCD_DATA(*CMD);
9
    USART_TX(*CMD++);
10
  }
11
}

Zum schluss, hier noch der Aufruf über eine Art state-machine. Der 
Subroutine wird der Pointer auf den jeweiligen string im struct 
übergeben.
1
while (1)
2
  {
3
    switch (STATE)
4
    {
5
      // init GSM module, just wait and send some CR
6
      case 0:
7
      _delay_ms(1000);
8
      _delay_ms(1000);
9
      _delay_ms(1000);
10

11
      USART_TX(0x0D);
12
      USART_TX(0x0D);
13
      USART_TX(0x0D);
14

15
      NEXT_STATE = 1;
16
      break;
17

18
      // send AT command to check state
19
      case 1:
20
      GSM_WRITE(AT_CMD_PTR -> AT);
21

22
      ERROR = GSM_CHECK();
23
      if (ERROR == 0) NEXT_STATE = 2;
24
      if (ERROR == 1) NEXT_STATE = 1;
25
      break;
26

27
      // turn off echo
28
      case 2:
29
      GSM_WRITE(AT_CMD_PTR -> ATE0);
30

31
      ERROR = GSM_CHECK();
32
      if (ERROR == 0) NEXT_STATE = 3;
33
      if (ERROR == 1) NEXT_STATE = 2;
34
      break;
35

36
      // send PIN code
37
      case 3:
38
      GSM_WRITE(AT_CMD_PTR -> AT_CPIN);
39

40
      ERROR = GSM_CHECK();
41
      if (ERROR == 0) NEXT_STATE = 4;
42
      if (ERROR == 1) NEXT_STATE = 3;
43
      break;
44

45
      // set SMS to text mode (1)
46
      case 4:
47
      GSM_WRITE(AT_CMD_PTR -> AT_CMGF);
48

49
      ERROR = GSM_CHECK();
50
      if (ERROR == 0) NEXT_STATE = 5;
51
      if (ERROR == 1) NEXT_STATE = 4;
52
      break;
53

54
      // set SMS notification and storage (1,1,0,2,1)
55
      case 5:
56
      GSM_WRITE(AT_CMD_PTR -> AT_CNMI);
57

58
      ERROR = GSM_CHECK();
59
      if (ERROR == 0) NEXT_STATE = 6;
60
      if (ERROR == 1) NEXT_STATE = 5;
61
      break;
62

63
      // idle state, wait for input from GSM (SMS)
64
      case 6:
65
      while(1) BLINK();
66
    }
67

68
    // set current state to next state
69
    STATE = NEXT_STATE;
70
  }

Es gibt noch viel mehr code mit den ganzen LCD subroutinen, aber ich 
glaube das hat mit meinem probmel nichts zum tun. Wenn ich den code so 
compiliere und an den Attiny2313 schicke, dann kommt ein ganz komisches 
verhalten raus. Die Pointeradressen, die der subroutine übergeben 
werden, stimmen nicht, die Ausgabe an der rs232 schnittstelle sieht dann 
so aus:

Statt dem CPIN string: #Q
Statt dem CNMI string: <>i
Statt dem CMGF string: 11AT+CNMI=1

und so weiter. Es werden also irgendwelche chars übertragen. teilweise 
dann auch wieder halbe strings. So, jetzt der komische / verwirrende 
Teil. wenn ich vor dem struct, ein dummy array mit mindestens 2 x 10 
elementen definiere (trial and error gg), funktioniert alles 
einwandfrei:
1
unsigned char DUMMY[2][10] = {"X","X"};
2

3
// initialise struct and define pointer
4
CHAR_ARRAY AT_CMD =
5
{
6
  "AT\0",
7
  "ATE0\0",
8
  "AT+CPIN=4053\0",
9
  "AT+COPS=?\0",
10
  "AT+CMGF=1\0",
11
  "AT+CNMI=1,1,0,2,1\0",
12
  "AT+CMGR=0\0",
13
  "AT+CMGS=\"+436645416393\"\0",
14
  "HZG EIN!\0",
15
  "HZG AUS!\0"
16
};

Womit kann das zusammenhängen? wird das struct vielleicht in 
irgendwelchen nicht zulässigen speicherbereichen definiert? Ich hoffe 
die Frage ist so halbwegs verständlich :)

Vielen Dank im Voraus!

Daniel
#2311388
Lesenswert?

Ja, und noch dazu:
Evtl. (insbesondere, wenn die Strings alle konstant sind), ist es auch 
viel einfacher, gar nicht sie in der struct zu halten, sondern dort nur 
Zeiger abzulegen (const char*).
Dann brauchst du gar nicht zu zählen, wie lang sie sind; der Compiler 
macht den Rest.
OP #2311420
Lesenswert?

Guten Morgen!

Danke für die schnellen Antworten! Also wenn ich das Array "AT" im 
Struct definiere, hat das die größe 3 obwohl nur 2 chars enthalten sind? 
Das '\0' Zeichen wird also automatisch hinzugefügt?

Erklährt diese falsche größendefinition auch das komische verhalten bei 
der ausgabe der strings?

@Klaus: Hast du für für die abgelegten Zeiger vielleicht ein kurzes 
code-schnipsel als beispiel? Ich bin leider noch nicht so fit was 
pointer betrifft  :)

Vielen Dank!
#2311624
Lesenswert?

Daniel K. schrieb:
> Erklährt diese falsche größendefinition auch das komische verhalten bei
> der ausgabe der strings?

Nein, das ist nicht die Ursache. Und die Größendefinition ist auch nicht 
falsch. Bei
1
char a[3] = "AT\0";
wird die automatische Null weggeschnitten und das Endergebnis ist exakt 
das gleiche wie beim "korrekteren"
1
char a[3] = "AT";
oder
1
char a[] = "AT";

Daniel K. schrieb:
> wenn ich vor dem struct, ein dummy array mit mindestens 2 x 10
> elementen definiere (trial and error gg), funktioniert alles
> einwandfrei:

Dann hast du vermutlich irgendeine Art von Memory-Corruption, z.B. ein 
Out-Of-Bounds Schreibzugriff auf ein Array.
#2311664
Lesenswert?

Daniel K. schrieb:

> @Klaus: Hast du für für die abgelegten Zeiger vielleicht ein kurzes
> code-schnipsel als beispiel? Ich bin leider noch nicht so fit was
> pointer betrifft  :)

Dann solltest du um die Dinger einen Bogen machen.
Bis jetzt konnte ich deinem Beispiel nicht wirklich entnehmen, wozu du 
da überhaupt eine struct brauchst und warum du auf die struct über einen 
Pointer zugreifst.

Alternativ kannst und solltest du natürlich die Dinge allerdings auch 
richtig lernen. Mit STrings, und damit auch mit Pointern, korrekt 
hantieren zu können ist eine der Grundfertigkeiten in C

So würde das aussehen
1
// define struct with all AT commands
2
typedef struct
3
{
4
  char* AT;
5
  char* ATE0;
6
  char* AT_CPIN;
7
  char* AT_COPS;
8
  char* AT_CMGF;
9
  char* AT_CNMI;
10
  char* AT_CMGR;
11
  char* AT_CMGS;
12
  char* SMS_TEXT_ON;
13
  char* SMS_TEXT_OFF;
14
} CHAR_ARRAY;
15

16
// initialise struct and define pointer
17
CHAR_ARRAY AT_CMD =
18
{
19
  "AT",
20
  "ATE0",
21
  "AT+CPIN=4053",
22
  "AT+COPS=?",
23
  "AT+CMGF=1",
24
  "AT+CNMI=1,1,0,2,1",
25
  "AT+CMGR=0",
26
  "AT+CMGS=\"+436641111111\"",
27
  "HZG EIN!",
28
  "HZG AUS!"
29
};

Das würde dem ganze dann wenigstens die Wendung geben, die eine 
derartige Struktur rechtfertigen würde: Für unterschiedliche Geräte 
sehen die Strings anders aus und haben natürlich auch andere Längen, was 
mit dieser Lösung kein Problem mehr darstellt.

Aber wie Stefan schon sagte: Das ist nicht dein eigentliches Problem. Da 
muss in deinem Programm noch was anderes faul sein.


PS: Deine konsequente Grossschreibung aller Variablennamen ist keine 
gute Idee. Wenn es eine Übereinkunft gibt, an die sich tatsächlich mehr 
oder weniger alle C-Programmierer weltweit halten, dann ist es die, 
dass Namen komplett in Grossbuchstaben ausschliesslich für Makros 
reserviert sind. Im Code kann es einen Unterschied machen, ob man es mit 
einem echten Makro oder mit einer echten Funktion zu tun hat, so dass es 
an manchen Stellen lebenswichtig sein kann, diese Information schnell zu 
sehen. Die Konvention von "ausschliesslich Grossbuchstaben sind ein 
Kennzeichen für ein Makro" erfüllt diese Information, so dass es Sinn 
macht sich an diese Konvention zu halten.
Auch du solltest das tun. Denn auf lange Sicht gesehen erzeugst du mit 
einer anderen Konvention (deiner eigenen) nur Konfusion.
Gast #2311683
Lesenswert?

Im Programm ist die Verwendung der Struktur faul. Eine Struktur ist kein 
Array und somit zieht sowas wie *CMD++ nicht. Damit würde dein Programm 
auf speicher zurück greifen der hinter deine Struktur liegt. Also leg 
dir lieber ein Array mit Pointern auf const char an dann kannst du 
solche späße wie CMD++ machen.

Gruß Stefan
OP #2311824
Lesenswert?

Hallo Leute!

wow, sehr viel input, danke euch. Das ist mein erstes Atmel C Programm, 
habe früher immer PICs mit ASM programmiert, deshalb noch die ganzen 
"Unschönheiten" im code.

Ist es theoretisch nicht egal ob man einzelne Arrays anlegt, die die 
ganzen Strings enthalten oder die alle zusammen in einem struct 
unterbringt? Der Hintergrund für das struct war eigentlich der ein 
Artikel über Codeoptimierung. Die Idee des Zusammefassens von Variablen 
in einem Struct hat mir gut gefallen:

http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Statische_.28globale.29_Variablen_in_einer_Struktur_sammeln

Zu den Variablennamen: Danke für den tip. wollte hier natürlich nicht 
für verwirrung sorgen. Wird auf jeden Fall beim nächsten Projekt 
berücksichtigt.

Ich werde heute Abend das Programm mal auf einem anderen Attiny laufen 
lassen um zu sehen, ob das unerklärbare Verhalten immer noch auftritt.

Daniel
#2311831
Lesenswert?

Daniel K. schrieb:

> Ist es theoretisch nicht egal ob man einzelne Arrays anlegt, die die
> ganzen Strings enthalten oder die alle zusammen in einem struct
> unterbringt?

Mit der jetzigen struct Lösung (die mit den Pointern) verbrauchst du 
mehr Speicher. Die vorhergehende struct Lösung hatte das Problem, dass 
ein Ändern der Strings viel Arbeit nach sich zieht.

> Der Hintergrund für das struct war eigentlich der ein
> Artikel über Codeoptimierung. Die Idee des Zusammefassens von Variablen
> in einem Struct hat mir gut gefallen:
>
> 
http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Statische_.28globale.29_Variablen_in_einer_Struktur_sammeln
>

Vergiss den Artikel. Besser gesagt diesen einen Abschnitt über 
Strukturvariablen. Das dort gezeigte Beispiel ist nicht repräsentativ, 
weil dieses gewählte Beispiel etwas ausser acht lässt: Die 3 Variablen 
sind nicht voneinander unabhängig, sondern haben als Uhrzeit einen 
logischen Zusammenhalt. Den hast du mit deinen Strings aber nicht.
Persönliche Seite #2312825
Lesenswert?

Alleine die ganzen Strings brauchen bei einem ATTiny2313 (oder welcher 
Tiny ist da drin?) über 80% vom Ram. Nachdem die gezeigten Sourcen nicht 
komplett sind, ist es durchaus möglich, dass es noch wesentlich mehr 
ist. Somit tippe ich auf einen Stackoverflow.

Dass der gcc bis zu eine \0 am Stringende automatisch wegschneidet hab 
ich noch nicht gewusst. Stimmt aber, habs gerade ausprobiert. Erst wenn 
mehrere \0 am Ende sind, sind die auch im Code enthalten.

Die ganzen Texte sollten im Flash abgelegt werden und die Fehler mit den 
Pointern behoben werden (es gibt beim Compilieren von obigem Code 
mindestens 2 Warnings wegen falschen Datentypen).

Grüße,

Peter

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren