Forum: Mikrocontroller und Digitale Elektronik Problem mit Pointern


von astroscout (Gast)


Lesenswert?

Hallo,
ich habe die Poiter zwar weitgehend verstanden, aber irgendwie stehe ich 
gerade auf dem Schlau :-)

Ich will den Inhalt (oder genauer den Pointer auf den Inhalt), der auf 
meinem LCD dargestellt werden soll, in einer Globalen Variable 
speichern. Die ist notwendig, da ich per ISR (durch einen Taster 
ausgelöst) kutzzeitig einen anderen Text auf dem LCD darstellen will, 
beim verlassen der ISR soll jedoch der ursprünglich Text wieder 
angezeigt werden.
Leider zeigt das Display beim Rücksprung zum ursprünglichen Text nur 
"Müll" an.

Könnt Ihr mir helfen??





Hier mal eine Code-Schnipsel:

In display soll der der dargestellte text (bzw. der Pointer dahin) 
gespeichert werden:
1
char* display;


Diese Prozedur stellt den Inhalt, der übergeben wurde, auf dem LCD dar
1
/*Sendet den komletten Display-Inhalt zum LCD (Array-Pointer)*/
2
void lcd_display(const char *data ,uint8_t center)
3
{
4
  lcd_clear();
5
  _delay_ms(5);
6
  int start = 1;
7
  display = data;   //Hier soll in display der anzuzeigende Inhalt gespecihert werden
8
  for (int i=0;i<4;i++){
9
    int laenge = strlen(data);
10
    if (center && laenge < 20) start = (20-laenge)/2 + 1;
11
    
12
    lcd_setcursor(i+1,start);
13
    
14
    for (int j=0; j<20;j++)
15
    {
16
      if (*data != '\0') lcd_data(*data);
17
      data++;
18
19
    }
20
    
21
    
22
  }
23
}

aufgerufen wird die Funktion z.B. folgendermaßen
1
char switch_off[4][20] = {"Anlage wird ","heruntergefahren","","Bitte warten..."};
2
lcd_display(*switch_off, 1);


Hier noch die ISR
1
/*Interupt-Service-Routine für Power-Taster*/
2
SIGNAL(SIG_INTERRUPT2)
3
{
4
  cli();
5
6
  if (power_status & (get_taster_zustand(PWR)))
7
  {
8
    int i = 3;
9
    char switch_off[4][20] = {"Anlage wird in","3 Sekunden","abgeschaltet....",""};
10
    lcd_display(*switch_off, 1);  //hier soll der neue Text dargestellt werden
11
    while((i>0) & get_taster_delay(PWR, 1000,0))
12
    {
13
      i--;
14
      lcd_setcursor(2, 6);
15
      lcd_data(i+48);
16
    }    
17
    if (!i) shutdown();
18
  } else {
19
    if(get_taster_delay(PWR, 1000,0))
20
    {
21
      sleep_disable();
22
      wakeup();
23
    }
24
  }
25
  lcd_display(*display, 1);   //Hier soll der alte Text wieder angezeigt werden
26
    
27
}
Im Vorraus schonmal vielen Dank für Eure Hilfe!!!

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Das "LCD-Ausgabe innerhalb der ISR" hört sich nicht gut an.

Diese Aufgabe wird üblicherweise aus gutem Grund anders programmiert. 
Z.B. Die ISR übergibt dem Anwendungsprogrammteil ein Flag und der 
Anwendungsprogrammteil kümmert sich darum.

von Andreas B. (Gast)


Lesenswert?

astroscout schrieb:
> char* display;
...
> /*Sendet den komletten Display-Inhalt zum LCD (Array-Pointer)*/
> void lcd_display(const char *data ,uint8_t center)
> {
...
>   lcd_display(*display, 1);   //Hier soll der alte Text wieder angezeigt werden

Warum nicht einfach mal lesen, was der Compiler zu sagen hat? Diesen 
Fehler hat er ganz bestimmt angemotzt.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Abgesehen davon machst du in lcd_display() selbst eine Zuweisung nach 
display. Der vorherige Inhalt von display wird also durch 
lcd_display(*switch_off, 1); zerstört und kann an der Stelle 
lcd_display(*display, 1); nicht mehr verwendet werden.

von astroscout (Gast)


Lesenswert?

Danke für eure schnellen Antworten:
@Stefan B.:
Ich gebe dir da völlig recht, ist nicht sehr schön, aber hier ist der 
Interrupt zeitunkritisch, sodass ich das so gelöst habe ;-)




@Andreas B.:
Der kompiler gibt mir folgendes aus:

In file included from lcd.h:88,
                 from main.c:44:
lcd.c: In function 'lcd_display':
lcd.c:238: warning: assignment discards qualifiers from pointer target 
type
In file included from taster.h:61,
                 from main.c:45:
taster.c: In function '__vector_3':
taster.c:189: warning: passing argument 1 of 'lcd_display' makes pointer 
from integer without a cast

leider hilft mir die aber nicht wirklich weiter :-(

von Oliver (Gast)


Lesenswert?

Du solltest das Programmkonzept nochmals überdenken.

Nach der Rückkehr aus der ISR musst du die Ausgabe des 
Hauptprogramm-Textes komplett neu starten, weil sich ja in der ISR 
zwischenzeitlich der Zustand des LCDs geändert hat. Das aber geht mit 
deinem Ansatz nicht.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Das hier

    lcd_display(*switch_off, 1);

kann auch nicht stimmen.

Holla!

Du hast versucht, dich hier
1
void lcd_display(const char *data ,uint8_t center);
2
3
....
4
5
    char switch_off[4][20] = {"Anlage wird in","3 Sekunden","abgeschaltet....",""};
6
    lcd_display(*switch_off, 1);  //hier soll der neue Text dargestellt werden

um einen Typfehler drumherum zu schwindeln, und jetzt kriegst du die 
Rechnung dafür präsentiert.

von Karl H. (kbuchegg)


Lesenswert?

Deine

 void lcd_display(const char *data ,uint8_t center)

ist überhaupt höchst ungeschickt programmiert.

Du solltest dir zum Ziel setzen, dass du sowas:
1
char switch_off[4][20] = {"Anlage wird in","3 Sekunden","abgeschaltet....",""};

in der Komplexität, gar nicht brauchst.
Das Argument zu lcd_display muss aus 4 Teiltexten bestehen, die 
hintereinander im Speicher abgelegt sind, wobei jeder Teiltext aus 20 
Buchstaben (+abschliessendem 0 Byte) bestehen muss.

Tut es das nicht, dann machts bei dir kaboom. Da sind Fehler 
vorprogrammiert.

Es wäre doch viel schöner, wenn du sowas machen könntest
1
char switch_off[] = {"Anlage wird in\n3 Sekunden\nabgeschaltet...." };

und lcd_display kümmert sich selber darum wieviele Zeilenumbrüche da im 
String enthalten sind und wie sie auszuwerten und anzuzeigen sind. 
Denkst du nicht?
Kein Aufrufer muss mehr darauf aufpassen, dass er dem lcd_display das 
richtige vorwirft. Keine Längenangaben, die stimmen müssen. Und das 
beste am Ganzen: ein String - ein Pointer.

von astroscout (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Das hier
>
>     lcd_display(*switch_off, 1);
>
> kann auch nicht stimmen.


okay, wie müsste es denn "sauber" lauten?

von astroscout (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> und lcd_display kümmert sich selber darum wieviele Zeilenumbrüche da im
> String enthalten sind und wie sie auszuwerten und anzuzeigen sind.
> Denkst du nicht?

ja, das geben ich dir recht, aber wie kann ich das sinnvoll 
realisieren??

von Karl H. (kbuchegg)


Lesenswert?

astroscout schrieb:
> Karl heinz Buchegger schrieb:
>> Das hier
>>
>>     lcd_display(*switch_off, 1);
>>
>> kann auch nicht stimmen.
>
>
> okay, wie müsste es denn "sauber" lauten?


Sauber?
Sauber übernimmt lcd_display nicht einfach nur einen String sondern ein
1
void lcd_display(const char data[4][20] ,uint8_t center);

Dein Problem ist hier, dass du einen wilden Mischmasch aus 2D Arrays mit 
1D Arrays veranstaltet hast, der nur deswegen 'funktioniert' weil der 
Pointer auf den Anfang eines 2D Arrays auch gleichzeitig der Pointer auf 
den Anfang der ersten Zeile dieses 2D Arrays ist.


Oder aber natürlich, die wirst diese ganzen 2D Array los, indem du noch 
ein wenig Intelligenz in lcd_display steckst. Dann kannst du dem einen 
beliebigen String vorwerfen, mit Formatierzeichen (dem \n) und 
lcd_display macht das Richtige damit.

von Karl H. (kbuchegg)


Lesenswert?

astroscout schrieb:
> Karl heinz Buchegger schrieb:
>> und lcd_display kümmert sich selber darum wieviele Zeilenumbrüche da im
>> String enthalten sind und wie sie auszuwerten und anzuzeigen sind.
>> Denkst du nicht?
>
> ja, das geben ich dir recht, aber wie kann ich das sinnvoll
> realisieren??

Im Prinzip ist gar nicht mal soviel Unterschied zu dem was du hast.
Nur kannst du nicht mehr strlen zur Bestimmung der Länge einer Zeile 
benutzen. :-)
Und natürlich überall, wo in deinem jetzigen Code 4 oder 20 steht musst 
du auf die tatsächlichen Gegebenheiten Rücksicht nehmen und nicht 
einfach nur Zahlenwerte voraussetzen. Aber ist ja kein Problem: while 
Schleifen sind ja schon erfunden :-)

von Karl H. (kbuchegg)


Lesenswert?

zb so
1
#define DISPLAY_WIDTH 20
2
3
void lcd_display(const char *data ,uint8_t center)
4
{
5
  uint8_t start = 1;
6
  uint8_t i = 1;
7
  
8
  lcd_clear();
9
  _delay_ms(5);
10
//  display = data;   //Hier soll in display der anzuzeigende Inhalt gespecihert werden
11
12
  while (*data != '\0') {
13
  
14
    if (center) {
15
      // Länge der nächsten Zeile feststellen
16
      uint8_t laenge = 0;
17
      const char* tmp = data;
18
      while( *tmp && *tmp != '\n' ) {
19
        laenge++;
20
        tmp++;
21
      }
22
        
23
      if (laenge < DISPLAY_WIDTH)
24
        start = (DISPLAY_WIDTH-laenge)/2 + 1;
25
    }
26
    lcd_setcursor(i, start);
27
    
28
    while (*data && *data != '\n') {
29
      lcd_data(*data);
30
      data++;
31
    }
32
33
    if (*data == '\n')
34
      data++; 
35
  }
36
}

von Andreas B. (Gast)


Lesenswert?

astroscout schrieb:
> taster.c:189: warning: passing argument 1 of 'lcd_display' makes pointer
> from integer without a cast
>
> leider hilft mir die aber nicht wirklich weiter :-(

Um das spezielle Problem noch mal aufzulösen (warum man das alles 
sowieso anders macht haben schon andere erläutert):

astroscout schrieb:
> char* display;

Display ist ein Zeiger auf char…

> lcd_display(*display, 1);   //Hier soll der alte Text wieder angezeigt werden

…also ist *display ein char (das * folgt dem Zeiger), in diesem Fall das 
erste Zeichen im String, auf den display zeigt. Das erste Argument von 
lcd_display ist aber ein Zeiger auf einen String, also wird das Zeichen 
als Zeiger interpretiert — und davor hat der Compiler gewarnt.

Dass dann Müll angezeigt wird ist nicht verwunderlich, es werden ja 
Daten ab einer Startadresse zwischen 0 und 255 (der Wertebereich eines 8 
bit char in Zeiger umgewandelt) als String interpretiert.

von astroscout (Gast)


Lesenswert?

Danke für Eure schnelle Hilfe
Ich habe mir jetzt die Display-Funktione so ungeschrieben, dass ich sie 
z.B. mit
1
lcd_display("Anlage wird in\n3 Sekunden\nabgeschaltet....", 1);

aufrufen kann.
Auch das speihern des Displayinhaltes funktioniert nun einwandfrei.

Eine Frage habe ich aber noch.
Wie kann ich zwei Teilstring miteinander verbinden?
In php gibt es ja dafür den Punktoperator, ich kann aber in C nichts 
dazu finden!

von Karl H. (kbuchegg)


Lesenswert?

astroscout schrieb:

> Eine Frage habe ich aber noch.
> Wie kann ich zwei Teilstring miteinander verbinden?
> In php gibt es ja dafür den Punktoperator, ich kann aber in C nichts
> dazu finden!

Du brauchst euin C Buch.
Ernsthaft

http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F

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.