Forum: Mikrocontroller und Digitale Elektronik Array und AVR-Studio


von JoBo70 (Gast)


Lesenswert?

Moin zusammen,
ich hänge seit einiger Zeit mit folgendem Problem an meiner 
Programmierung eines ATMEGA8 mit AVR-Studio 5 fest und komme einfach 
nicht weiter. Bevor entsprechende Kommentare aufkommen: Ja, ich habe das 
Forum vorher durchsucht und ja, ich habe auch ein C-Buch gelesen. 
Manchmal sitzt man aber einfach stundenlang vor einem Problem und findet 
den entscheidenden Bug nicht. Vielleicht ist es hier auch so:
Mein Programm soll ein GSM-Modul über den UART initialisieren und - wenn 
die Befehle einwandfrei ausgeführt wurden - ein OK zurückerhalten. Die 
folgende Routine läuft mit Codeblocks aus dem PC einwandfrei, auf dem 
AVR funktioniert dagegen der Teil mit der Füllung des Array mit 0x41 
(nur zum testen, später laufen hier die empfangenen Bytes vom UART rein) 
nicht. Für die Ausgabe auf dem AVR nutze ich natürlich nicht printf, 
sondern eine Ausgaberoutine für ein  LCD, die jedoch schon funktioniert 
(lcd_out(char *s)).
Ich bekomme derzeit auf dem LCD den ursprünglichen Text "Hallo Welt" 
ausgegeben, nicht aber die A's (0x41), die die Schleife ins Array 
schreibt, d. h. das Array wird in der Funktion nicht geändert.
Hat jemand eine Idee, woran's liegen könnte?

Danke Euch schon vorab!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void read_uart(char *str)
{
   unsigned int i=0;
   unsigned int c=0x41;

   for (i=0;i<15; i++)              //Hier steht später eine Abfrage á 
la while(solange empfang mache weiter)
   {
       str[i] = (unsigned char)c;   //Hier füllt später die uart_getc 
von Peter Fleury das Array
   }
}


void gsm_init(char *abuffer)
{
    //Hier laufen einige Initialisierungsbefehle für das GSM-Modul ab, 
die Bestätigung kommt vom Modul mit "OK".
    read_uart(abuffer);         //Frage die UART-Routine ab, ob OK vom 
Modul eingetroffen
    printf("%s\n",abuffer);
}


int main(void)
{
   char buffer[15];
   char *pbuffer;
   memset (buffer, 0, 15);

   pbuffer = &buffer[0];
   *pbuffer = "Hallo Welt";     //Schreibt etwas in Array, um zu sehen, 
ob die Pointer einwandfrei arbeiten

   gsm_init(pbuffer);           //Ruft die Initialisierung auf
   return EXIT_SUCCESS;
}

von Jesse (Gast)


Lesenswert?

Müsste str[] nicht global sein, damit die Funktion es ändern kann?

von Michael (Gast)


Lesenswert?

JoBo70 schrieb:
> d. h. das Array wird in der Funktion nicht geändert.
Bist du da sicher?

> ja, ich habe auch ein C-Buch gelesen
Hast du auch den Abschnitt über Strings gelesen, wo der Abschluß durch 
ein 0x00-Byte beschrieben ist. Am Ende deiner "a"-Füllroutine muß das 
noch passieren.

von JoBo70 (Gast)


Lesenswert?

Eigentlich nicht, die Funktion soll ja bewusst auf das Array als Adresse 
zurückgreifen (Pointer) und den Inhalt über die Adresse ändern. Meines 
Wissens nach sollte man doch nur so wenig globale Variablen definieren 
wie möglich, oder?

von ... (Gast)


Lesenswert?

Jesse schrieb:
> Müsste str[] nicht global sein, damit die Funktion es ändern kann?
Wozu gibt es Parameterübergabe "by Reference"? Nichts anderes ist der 
Pointer *Str. Steht auch im C-Buch.

von JoBo70 (Gast)


Lesenswert?

Hallo Michael,

danke für den Hinweis. Du hast recht, der Abschluss fehlt. Habe ich 
gerade mal mit

str[i+1] = '\0';

am Ende eingefügt, aber bringt leider keine Änderung ..

von Michael (Gast)


Lesenswert?

JoBo70 schrieb:
> am Ende eingefügt, aber bringt leider keine Änderung ..
Hast du in deinem Puffer auch genügend Platz für 15 Zeichen + '\0'?

von T. Reinisch (Gast)


Lesenswert?

Hallo,

JoBo70 schrieb:
> *pbuffer = "Hallo Welt";

also, das tut schon mal nicht das, was Du denkst.

String Literal in Zuweisung bedeutet Adresse des String-Literals, Typ in 
diesem Fall ein char[11] Pointer

*pbuffer = "Hallo Welt"

bedeutet also schonmal

*pbuffer = Adresse der Stelle wo das H von Hallo Welt steht

Links wird pbuffer vom Typ char* dereferenziert. pbuffer enthält zu 
diesem Zeitpunkt die Adresse der Speicherzelle buffer[0].

Dereferenziert ergibt das ein char.  Diesem char weist Du einen char[11] 
Pointer zu, das ergibt nie und
nimmer das Ergebnis, das Du erwartet hast! Der Compiler hätte auch 
warnen müssen.

Das am Ende dennoch Hallo Welt ausgegeben wird, ist ein Zeichen dafür, 
dass in Deiner LCD Ausgabe irgendwo was nicht stimmt. Es kommt schon 
AAAAAAAAA dabei raus, wenn alles richtig läuft, weil die Hallo Welt 
Zeile komplett wirkungslos ist.

Also: Der Fehler muss in der Ausgabe liegen!

Vlg

 Timm

von bitte löschen (Gast)


Lesenswert?

JoBo70 schrieb:
> *pbuffer = "Hallo Welt";

Mööööp!
pbuffer ist ein Zeiger auf char und wird mit
pbuffer = &buffer[0];
initialisiert.

*pbuffer bedeutet also das erste Element des Arrays.
Diesem ersten Element weist Du nun die Adresse eines Strings ("Hallo 
Welt") zu.

Das macht nichts kaputt oder so, aber führt bestimmt nicht dazu, dass 
das Array mit der Bytefolge "Hallo Welt" gefüllt wird.
Schau mal nach strcpy oder memcpy.

von JoBo70 (Gast)


Lesenswert?

Hallo T.Reinisch,
danke für die ausführliche Erklärung.
Meine LCD-Ausgabe sieht wie folgt aus:

void lcd_out(char *s)
{
     while (*s) //so lange Zeichen ausgeben wie *s != '\0', also 
ungleich dem "String-Endezeichen"
  {
          lcd_write(*s, 1);          //Zeichen ausgeben
    s++;
  }
}

WEnn ich die Routine per lcd_out("Hallo Welt") direkt anspreche, 
funktioniert sie einwandfrei. Wo kann in der kleinen Routine der Fehler 
denn stecken?

von T. Reinisch (Gast)


Lesenswert?

Hallo,

ich habs nur mal zusammengefasst und in main ein  wenig aufgeräumt, das 
ist also jetzt der Stand: was liefert das nun bei dir?

vlg

 Timm

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void lcd_out(char *s)
{
    while (*s)    {
        //  lcd_write(*s, 1);
        printf("%c",*s);    // bzw eben lcd_write
        s++;
    }
}

void read_uart(char *str)
{
    unsigned int i=0;
    unsigned int c=0x41;

    for (i=0;i<14; i++)
    {
            str[i] = (unsigned char)c;
    }
    str[14] = '\0';
}


void gsm_init(char *abuffer)
{

    read_uart(abuffer);
    lcd_out(abuffer);
}


int main(void)
{
    char buffer[15];
    memset (buffer, 0, 15);

    strcpy(buffer,"Hallo Welt!\0");

    gsm_init(buffer);
    return EXIT_SUCCESS;
}

von JoBo70 (Gast)


Lesenswert?

Hallo T.Reinisch,

danke für den Vorschlag. Auf dem PC läuft's einwandfrei - er gibt 14 A's 
aus.

Auf dem ATMEGA läuft's jetzt auch:
Ich hatte zuvor auf dem ATMEGA in der Routine gsm_init den Aufruf 
lcd_out(*abuffer) verwendet, könnte das der entscheidende Fehler gewesen 
sein? Mit lcd_out(abuffer) läuft's jetzt.

Ich habe bei der Gelegenheit noch ein wenig "rumgespielt" und wenn ich 
statt

strcpy (buffer, "Hallo Welt!\0");

das hier einsetze
buffer[0] = "Hallo Welt\n";

funktioniert's auch. Ist beides eventuell gleichwertig?

von T. Reinisch (Gast)


Lesenswert?

Hallo,

prima, dass es jetzt funktioniert.

Ja das könnte der entscheidende Fehler sein. Das Problem mit den 
Pointern ist einfach, dass nicht garantiert ist, dass das Programm nicht 
funktioniert, wenn man etwas falsch macht. Es funktioniert dann halt nur 
manchmal oder tut sehr seltsame Dinge.

Gefühlt glaube ich, dass je kleiner der Adressraum ist, die 
Wahrscheinlichkeit um so größer ist, dass bei Pointerfehlern noch 
irgendetwas halbwegs sinnvolles passiert.

Aber was ist das für ein Kompilier, dass der diesen Aufruf ohne Warnung 
schluckt? Ich kenne WinAVR nicht, aber irgendwo muss man ihm sagen 
können "-Wall" das ist wichtig. Oder gibt der Warnungen aus und du 
ignorierst sie nur? Das solltest Du nicht tun!

Wenn Du gecheckt hast, wo Du -Wall eintragen musst, kannst Du gleich 
noch -Werror setzen. Für den Anfang sicherlich eine prima Sache!


JoBo70 schrieb:
> Ich habe bei der Gelegenheit noch ein wenig "rumgespielt" und wenn ich
> statt
>
> strcpy (buffer, "Hallo Welt!\0");
>
> das hier einsetze
> buffer[0] = "Hallo Welt\n";
>
> funktioniert's auch. Ist beides eventuell gleichwertig?

auch das sollte der Compiler Dir nicht durchgehen lassen.
links steht ein char und rechts ein char[11]. Das ist absolut nicht 
gleichwertig.

In C gibt es keinen Datentyp "Zeichenkette".

Das das funktioniert ist kein Beleg dafür, dass es immer funktioniert, 
sondern dafür, dass verdeckte und seltsame Effekte bei kleinen 
Adressräumen alles andere als selten sind. Verwende einfach den 
Standard-Konformen Weg über das explizite Kopieren mit strcpy oder 
memcpy  und Du kannst sicher sein, dass Dein Programm sich so 
vorhersagbar wie möglich verhält.

Durch solche Geheimoperationen mit ungewissem Ausgang kannst Du ganz 
tolle Fehler produzieren. Plötzlich funktioniert die Ausgabe doch nicht 
mehr, ob wohl du nichts geändert hast, oder nur einen Programmteil, der 
nichts damit zu tun hat.

Viele Grüße

 Timm

von JoBo70 (Gast)


Lesenswert?

Hallo Timm & und auch alle anderen,

lieben Dank für die schnelle Hilfe und die hilfreichen Erklärungen, die 
mir sehr geholfen haben!

Beste Grüße!

PS: Ich nutze AVR Studio 5.

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.