Forum: Mikrocontroller und Digitale Elektronik UART empfängt nur einmal.


von Eddy (Gast)


Lesenswert?

hallo,

ich will den uart benutzen um den status eines gsm modems einzulesen.
dazu sendet der µC ein AT+CPIN?
die antwort ist entweder +CPIN: SIM PIN oder +CPIN: READY .
die beiden fälle unterscheidet das programm schon.
jedoch möchte ich nach der pinausgabe nochmals abfragen ob alles okay 
ist und das modem sich im READY zustand befindet.

mein problem ist nun,
durch den goto befehl springt der µc im programm und durchläuft nochmals 
die if schleife, er erkennt jedoch nicht den READY status des modems 
sondern lässt die led blinken wie es in ELSE angegeben ist.
erst wenn ich den µc resette geht die gleichung der if schleife auf.

kurz gesagt beim ersten mal daten vom uart abfragen läuft wunderbar 
jedoch beim 2. mal (ohne neustart) steht in der variable pin 
warscheinlich nur noch kauderwelsch.

was mach ich falsch? muss ich ein uart reset einbauen? wenn ja wie?

grüße,
Eddy

CODE:::::
****************************************************************

void main()
{


  char pin[20];
  uart_ini();





  DDRA |= ( 1 << DDA0 ); //pin A0 auf ausgang
  DDRA |= ( 1 << DDA1 ); //pin A1 auf ausgang
  DDRA |= ( 1 << DDA2 ); //pin A3 auf ausgang

  abfrage:


  //pin==0;
  uart_clear();
  PORTA |= (1<<PA2); // Pin PA2 "high" //  Rot wärend des Datenempfangs
  uart_puts("AT+CPIN?\r");

  uart_gets(pin,12);
  PORTA &= ~(1<<PA2);// Pin PA2 "low" //   Rot


  if (strncmp (pin,"\r\n+CPIN: SIM PIN",11) == 0)
    {

  PORTA |= (1<<PA1); // Pin PA1 "high" //   Gelb für "Gerät Erkannt 
übertrage PIN"
  uart_puts("AT+CPIN=\"3971\"\r");
  delay_ms(20000);
  PORTA &= ~(1<<PA1);// Pin PA1 "low" //
  goto abfrage;
  }

  else if (strncmp (pin,"\r\n+CPIN: READY",11) == 0)
  {
  PORTA |= (1<<PA0); // Pin PA0 "high" //
  }

  else //---> wenn nichts zu trifft:: ERROR Blinken   (Rot)<-----
  {
  schleife:
  PORTA |= (1<<PA2); // Pin PA2 "high" //
  delay_ms(500);
  PORTA &= ~(1<<PA2);// Pin PA2 "low" //
  delay_ms(500);
  goto schleife;
  }
  //##################################################################

  //PORTA &= ~(1<<PA0);// Pin PA0 "low" //
  //PORTA &= ~(1<<PA1);// Pin PA1 "low" //

von Eddy (Gast)


Lesenswert?

1
void main()
2
{
3
4
5
  char pin[20];
6
  uart_ini();
7
8
9
10
11
12
  DDRA |= ( 1 << DDA0 ); //pin A0 auf ausgang
13
  DDRA |= ( 1 << DDA1 ); //pin A1 auf ausgang
14
  DDRA |= ( 1 << DDA2 ); //pin A3 auf ausgang
15
16
17
18
//****ab hier läuft das programm nochmals nach der pin eingabe****//
19
  abfrage2:
20
21
  
22
  uart_puts("AT+CPIN?\r");
23
  uart_gets(pin,12);
24
  
25
26
27
  if (strncmp (pin,"\r\n+CPIN: SIM PIN",11) == 0)
28
    {
29
30
  uart_puts("AT+CPIN=\"3971\"\r"); //Pin eingabe//
31
  delay_ms(20000);                 //Warten bis es NETZ hat //
32
  goto abfrage2;                    //Nochmal von vorne//
33
    
34
    }
35
36
  else if (strncmp (pin,"\r\n+CPIN: READY",11) == 0)
37
  {
38
  PORTA |= (1<<PA0); // Grüne lampe an denn es ist alles okay
39
  }
40
41
  else // wenn nichts zu trifft:: ERROR Blinken   (Rot) //
42
  {
43
  
44
  PORTA |= (1<<PA2); // Pin PA2 "high" //
45
  delay_ms(500);
46
  PORTA &= ~(1<<PA2);// Pin PA2 "low" //
47
  delay_ms(500);
48
49
  }

von fubar (Gast)


Lesenswert?

Aufgeräumt, aber nicht getestet
1
void main()
2
{
3
  char pin[20];
4
  uart_ini();
5
6
  //pin A0/A1/A2 auf ausgang, alle anderen auf input
7
  DDRA = ( 1 << DDA0 ) | ( 1 << DDA1 ) | ( 1 << DDA2 );
8
9
  
10
  for (;;)
11
  {
12
    uart_puts("AT+CPIN?\r");
13
    uart_gets(pin,12);
14
15
    if (strncmp (pin, "\r\n+CPIN: SIM PIN", 11) == 0)
16
    {
17
      //Pin eingabe
18
      uart_puts("AT+CPIN=\"3971\"\r");
19
      
20
      //Warten bis es NETZ hat
21
      delay_ms(20000);
22
      
23
      // springe zum anfang der for schleife
24
      continue;
25
    }
26
    else if (strncmp (pin, "\r\n+CPIN: READY",11) == 0)
27
    {
28
      PORTA |= (1<<PA0); // Grüne lampe an denn es ist alles okay
29
    }
30
    else
31
    {
32
      // wenn nichts zu trifft:: ERROR Blinken   (Rot)
33
      // Pin PA2 "high"
34
      PORTA |= (1<<PA2);
35
      // warte 500 ms
36
      delay_ms(500);
37
      // Pin PA2 "low"
38
      PORTA &= ~(1<<PA2);
39
      delay_ms(500);
40
    }
41
  }
42
}

von Karl H. (kbuchegg)


Lesenswert?

1. Der goto ist ein ziemlich Unnötiger Ballast an der Stelle.

  Warum nicht das Sprachmittel einsetzen, welches sich hier
  anbieten würde: Eine while Schleife?

  Frage das Händi ob es einen PIN braucht
  Solange das Händi nicht mit PIN READY antwortet {
     Schicke Pin
     Warte eine Zeit lang
     Frage das Händi ob es einen PIN braucht
  }

2. Was macht deine uart_get Routine, wenn es mehr Zeichen
   empfängt, als du abholst? Speichert es die überzähligen
   Zeichen zwischen oder verwirft es sie?

   Der Grund warum ich frage:
   uart_gets( pin, 12 );

   Hier sagst du der uart_gets, sie möge Zeichen empfangen, aber
   maximal nur 12 Stück (dazu gleich noch mehr). Deine Texte, auf
   die du vergleichst sind aber länger als 12 Zeichen. Also muss
   uart_gets ein paar Zeichen unter den Tisch fallen lassen, wenn
   es die Antwort liefert (oder zwischenspeichern um sie dann beim
   nächsten Aufruf von uart_gets zu liefern)
   Ausserdem: Warum 12? Dein Array pin ist doch mit 20 definiert!
   Das Array könnte also viel mehr aufnehmen als nur 12 Zeichen.

3. Zähl mal die Zeichen in "\r\n+CPIN: SIM PIN". Das sind mehr als
   11!
   Selbiges für den anderen String

4. Die Sache mit \r\n
   Wenn du nicht verifiziert hast, dass dein Händi (generell: das
   Gerät welches am anderen Ende der Leitung hängt) tatsächlich
   jeden Text mit \r\n abschliesst, dann ist deine beste Strategie,
   wenn uart_gets diese beiden Zeichen \r und \n gleich beim Empfang
   aus dem String ausfiltert, sodass du dich in weiterer Folge bei
   der Stringverarbeitung nicht mehr darum kümmern musst.
   Manche Geräte schicken \r \n
   Manche Geräte schicken \n \r
   Manche schicken nur \r
   Wieder andere schicken nur \n

   Aus diesem Wust an Möglichkeiten ist es am simpelsten wenn man
   zwar mit einem \n oder einem \r eine Eingabezeile als beendet
   erkennt, aber ansonsten werden die \n oder \r ignoriert und tauchen
   im empfangenen String nie auf.

von Eddy (Gast)


Lesenswert?

Nochmal ein bisschen anders ...
funzt aber immer noch nicht

merkwürdiges neues problem ist jetzt das wenn modem ready status ausgibt 
und der µC das erkennt blinken grün und rot abwechelnd......



1
  char pin[20];
2
  uart_ini();
3
4
  //pin A0/A1/A2 auf ausgang, alle anderen auf input
5
  DDRA = ( 1 << DDA0 ) | ( 1 << DDA1 ) | ( 1 << DDA2 );
6
7
  
8
  for (;;)
9
  {
10
    uart_puts("AT+CPIN?\r");
11
    uart_gets(pin,12);
12
13
    if (strncmp (pin, "\r\n+CPIN: SIM PIN", 11) == 0)
14
    {
15
      //Pin eingabe
16
      uart_puts("AT+CPIN=\"3971\"\r");
17
      
18
      //Warten bis es NETZ hat
19
      delay_ms(20000);
20
      
21
      // springe zum anfang der for schleife
22
      continue;
23
    }
24
    else if (strncmp (pin, "\r\n+CPIN: READY",11) == 0)
25
    {
26
      PORTA |= (1<<PA0); // Grüne lampe an denn es ist alles okay
27
    }
28
    else
29
    {
30
      // wenn nichts zu trifft:: ERROR Blinken   (Rot)
31
      // Pin PA2 "high"
32
      PORTA |= (1<<PA2);
33
      // warte 500 ms
34
      delay_ms(500);
35
      // Pin PA2 "low"
36
      PORTA &= ~(1<<PA2);
37
      delay_ms(500);
38
    continue;

von Eddy (Gast)


Lesenswert?

nochmals der code:
jetzt checkt ers erst wenn ich halt den reset button drück.
obwohl der danach ja auch nichts anderes macht.
ich checks net.

1
  char pin[20];
2
  uart_ini();
3
4
  //pin A0/A1/A2 auf ausgang, alle anderen auf input
5
  DDRA = ( 1 << DDA0 ) | ( 1 << DDA1 ) | ( 1 << DDA2 );
6
7
  
8
  for (;;)
9
  {
10
  PORTA |= (1<<PA2);
11
    uart_puts("AT+CPIN?\r");
12
    uart_gets(pin,12);
13
  PORTA &= ~(1<<PA2);
14
15
    if (strncmp (pin, "\r\n+CPIN: SIM PIN", 11) == 0)
16
    {
17
    PORTA |= (1<<PA1);
18
      //Pin eingabe
19
      uart_puts("AT+CPIN=\"3971\"\r");
20
      
21
      //Warten bis es NETZ hat
22
      delay_ms(20000);
23
      PORTA &= ~(1<<PA1);
24
      // springe zum anfang der for schleife
25
      continue;
26
    }
27
    else if (strncmp (pin, "\r\n+CPIN: READY",11) == 0)
28
    {
29
      PORTA |= (1<<PA0); // Grüne lampe an denn es ist alles okay
30
      for(;;);
31
  }
32
    else
33
    {
34
      // wenn nichts zu trifft:: ERROR Blinken   (Rot)
35
      // Pin PA2 "high"
36
      PORTA |= (1<<PA2);
37
      // warte 500 ms
38
      delay_ms(500);
39
      // Pin PA2 "low"
40
      PORTA &= ~(1<<PA2);
41
      delay_ms(500);
42
    continue;
43
    }
44
  }

hier sind meine anderen voids die ich verwende:
1
void uart_gets( char* Buffer, uint8_t MaxLen )
2
{
3
 
4
  uint8_t NextChar;
5
  uint8_t StringLen = 0;
6
 
7
  NextChar = ser_getc();         // Warte auf und empfange das nächste Zeichen
8
 
9
                                  // Sammle solange Zeichen, bis:
10
                                  // * entweder das String Ende Zeichen kam
11
                                  // * oder das aufnehmende Array voll ist
12
  while( NextChar != 'cr' && StringLen < MaxLen - 1 ) {   //    ---->>>\t<<<????
13
    *Buffer++ = NextChar;
14
    StringLen++;
15
    NextChar = ser_getc();
16
  }
17
 
18
                                  // Noch ein '\0' anhängen um einen Standard
19
                                  // C-String daraus zu machen
20
  *Buffer = '\0';
21
}
22
 
23
 
24
unsigned char ser_getc (void)    
25
{
26
  unsigned char c;
27
 
28
  while(!rbuffcnt);       // Warte bis ein Zeichen vorhanden ist
29
 
30
  cli();            // Interruptbehandlung kurz aussetzen. Ab jetzt muß es schnell gehen (wenig Befehle), damit Zeichen, die 
31
                // ab jetzt eintreffen nicht verloren gehen.
32
  rbuffcnt--;          // anschl. ein Zeichen weniger zum ausgeben
33
  c = rbuff [rbuffpos++];  // Zeichen holen, was nach dem bereits gelesenen liegt
34
  if (rbuffpos >= RBUFFLEN)  rbuffpos = 0;  // wenn hinterstes Zeichen (rechts im Puffer) gelesen wurde, dann wieder vorne anfangen
35
 
36
  sei();            // Interruptbehandlung wieder aktivieren
37
 
38
  return (c);        // Zeichen zurückgeben
39
}

von Karl H. (kbuchegg)


Lesenswert?

Da ist offensichtlich eine Interrupt getriebener Ringbuffer
dahinter.
Damit haben sich alle meine Bedenken bezüglich der nicht
abgeholten Zeichen bestätigt.

Soll ich es dir in Stein aufmeisseln oder glaubst du es mir auch
so:
Stell endlich deine String-Längen richtig!

von holger (Gast)


Lesenswert?

>  while( NextChar != 'cr' && StringLen < MaxLen - 1 ) {

Bei der Zeile dürfte der Compiler ziemlich meckern.
'' nimmt man nur für ein Zeichen, nicht zwei.

  while( NextChar != '\r' && StringLen < MaxLen - 1 ) {

von Stefan E. (sternst)


Lesenswert?

Zusätzlich zu dem was Karl Heinz geschrieben hat, hast du noch ein 
mögliches Problem. Auf das Senden der PIN "AT+CPIN=\"3971\"\r" kommt 
doch sicher auch eine Antwort, oder? Die dürfte beim nächsten 
Schleifendurchlauf auch noch in irgendwelchen Buffern rumgeistern (und 
wenn vielleicht auch nur teilweise).

von Eddy (Gast)


Lesenswert?

ja genau das ist mein problem! wie bekomm ich das weg?

von Eddy (Gast)


Lesenswert?

\r oder \n kann ich als zeilenende nicht nehmen weil das das gsm modem 
auch oft am anfang einer zeile einbaut!

von Stefan E. (sternst)


Lesenswert?

Eddy wrote:

> ja genau das ist mein problem!

Nicht nur das. Dass du bei dem uart_gets eine zu kleine Länge angibst, 
ist definitiv auch ein Problem.

> wie bekomm ich das weg?

Indem du die Antwort aus dem Buffer ausliest, auch wenn sie dich gar 
nicht interessiert.

> \r oder \n kann ich als zeilenende nicht nehmen weil das das gsm modem
> auch oft am anfang einer zeile einbaut!

Na ja, am Ende einer Antwort wird auch schon noch sowas kommen.

Zwei Dinge sind wichtig, damit du deine Probleme in den Griff bekommst.
1) Eine Änderung der Funktion uart_gets.
2) Ein geänderter Aufruf dieser Funktion.

Zu 1) würde ich folgendes vorschlagen (ungetestet):
1
void uart_gets( char* Buffer, uint8_t MaxLen )
2
{
3
 
4
  uint8_t NextChar;
5
  uint8_t StringLen = 0;
6
  uint8_t Flag = 0;
7
8
  do {
9
10
      NextChar = ser_getc();
11
12
      if (NextChar=='\r' || NextChar=='\n') {
13
          if (!Flag)
14
              continue;    // erstmal alle \r und \n am Anfang ignorieren
15
          else
16
              break;       // danach makiert das erste \r oder \n das Ende
17
      }
18
      else
19
          Flag = 1;
20
21
      *Buffer++ = NextChar;
22
      StringLen++;
23
24
  } while (StringLen < MaxLen-1);
25
26
  *Buffer = '\0';
27
}

Zu 2): Du musst die Funktion mit der Größe vom Buffer aufrufen, damit 
sichergestellt ist, dass auch die komplette Antwort gelesen wird.
Am besten so (dann kannst du die Größe schnell und sicher ändern):
1
#define BUF_SIZE 20
2
...
3
char pin[BUF_SIZE];
4
...
5
uart_gets(pin,BUF_SIZE);
6
...

Bei den Vergleichen lässt du dann die \r und \n weg.
Und wenn du nach dem Sender der PIN die Antwort ausliest, kannst du auch 
das "delay_ms(20000);" weglassen.

von Tim (Gast)


Lesenswert?

hat er dann nicht das problem das er immer die genaue länge des zu 
erwartenden strings angeben muss damit sein mikroprozessor nicht bis in 
nächste jahrtausend auf weitere zeichen wartet??

von Stefan E. (sternst)


Lesenswert?

Wenn die Antwort des GSM-Modems am Ende entweder ein \r oder ein \n 
(oder auch beides) enthält (und davon gehe ich aus), dann nicht.

Ich sehe aber gerade, dass das Flag in der Funktion ja überflüssig ist. 
Das passiert halt, wenn man den Code "mal eben auf die Schnelle" 
hinschreibt. ;-)
1
void uart_gets( char* Buffer, uint8_t MaxLen )
2
{
3
 
4
  uint8_t NextChar;
5
  uint8_t StringLen = 0;
6
7
  do {
8
9
      NextChar = ser_getc();
10
11
      if (NextChar=='\r' || NextChar=='\n') {
12
          if (!StringLen)
13
              continue;    // erstmal alle \r und \n am Anfang ignorieren
14
          else
15
              break;       // danach makiert das erste \r oder \n das Ende
16
      }
17
18
      *Buffer++ = NextChar;
19
      StringLen++;
20
21
  } while (StringLen < MaxLen-1);
22
23
  *Buffer = '\0';
24
}

von Eddy (Gast)


Lesenswert?

also:

vielen dank ersteinmal für die tatkräftige unterstützung!
das ganze projekt läuft jetzt soweit.


nun möchte ich ja auch gerne per sms befehle schicken können, auf die 
der µC dann reagiert.
dafür würde ich ja wie immer uart_gets() benutzem um etwas vom modem 
einzulesen.
jedoch weiß ich ja nicht wann eine sms eintrifft und um dies zu erkennen 
müsste der µC ja permanent an der seriellen schnittstelle auf text 
warten.
dies wäre mit dem aufruf von uart_gets() erledigt da die funktion erst 
beendet wird wenn die max string länge erreicht wurde oder ein string 
endzeichen erkannt wird.
eine ausgabe vom modem wäre z.B. +CMTI:"SM",3 diese zeile sagt mir das 
eine neue sms im speicherbereich 3 gespeichert wurde.
die darauf folgende antwort vom µC wäre dann (öffne SMS 3)-->AT+CMGR=3.
folglich wird der inhalt der sms ausgegeben mit dem sich dann arbeiten 
ließe.

mein problem ist nun:
nach dem aufruf der funktion uart_gets() wartet der µC auf zeichen. 
falls aber keine sms eintrifft empfängt der µC keine zeichen vom modem, 
wartet bis ins nächste jahrhundert und beendet nicht die uart_gets() 
funktion um mit dem weiteren programm fortzufahren.
heißt: in der zeit zwischen aufruf der funktion und eingehender sms 
beobachtet der µC nicht den status von pinX.
das ist aber fatal da der µC ja eine sms verschicken soll wenn sich was 
an pinX verändert.

kurz gesagt in zwei fällen soll reagiert werden:
wenn sich pinX verändert und wenn eine sms mit befehl eintrifft.

wie löse ich dieses problem?

von schurli (Gast)


Lesenswert?

Ich habe momentan genau das gleich Problem wie du!
Wie kann man auf einen String vom Modem warten, ohne dass der MC in eine 
Endlosschleife geht?

von Eddy (Gast)


Lesenswert?

ja ich hab auch noch nicht weitergebastelt bis jetzt
aber ich denke wir gehen das völlig falsch an.
das muss irgendwie mit interupts gemacht werden oder so so.. ;-)

wenn eine sms an kommt sendet das modem ja sofort einen string
und jedes andere gerät verarbeitet diesen ja auch sofort...irgendwie...

naja vielleicht sollte man es besser auf ne andere tour versuchen..
einfach in ner schleife am programm anfang oder ende den sms speicher 
auslesen und gucken ob was neues da ist .

aber ich denke sowieso das wenn mein µC etwas unerwartes empfängt gibts 
bei der nächsten modemabfrage probleme.

von Eddy (Gast)


Lesenswert?

poste mal wenn du was neues hast!

was baust du überhaupt?

von Matthias L. (Gast)


Lesenswert?

>Ich habe momentan genau das gleich Problem wie du!
>Wie kann man auf einen String vom Modem warten, ohne dass der MC in eine
>Endlosschleife geht?

Die Zauberworte sind:
-Interruptgesteuertes Empfangen mit (FIFO)-Puffer
und:
- Zustandsautomaten für den Empfang und die Auswertung

von Eddy (Gast)


Lesenswert?

ja das sind ja nette ansätze aber wie bekomm ich das denn jetzt in den 
code oben implementiert??

hat schon lang genug gedauert den scheiß da oben zum laufen zu 
bringen........

von Eddy (Gast)


Lesenswert?

und außerdem....

ich dachte das ganze würde schon mit ringbuffer laufen....
hatte zumindestens früher schon das problem das der noch irgendwelche 
"alten" zeichen  im puffer hatte und mir die dann beim auslesen mit 
ausgespuckt hat.

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.