Forum: Mikrocontroller und Digitale Elektronik AVR und Modem Problem


von f95 (Gast)


Lesenswert?

Hi ich habe mir eine Funktion geschrieben um mit einem Modem über die 
UART Schnittstelle zu kommunizieren.
1
//uart
2
void uart_init(unsigned int);
3
void uart_c(unsigned char);
4
void uart_str(char *);
5
unsigned char uart_getc(void);
6
void uart_get_str(char *);
7
8
//at
9
unsigned char at_send(char *, char *);
10
11
//at responding strings
12
char *_AT_pin_ready = "+CPIN: READY";
13
char *_AT_sim_pin = "+CPIN: SIM PIN";
14
char *_AT_ok = "OK";
15
16
int main(void)
17
{  
18
  init_io();  
19
  uart_init(MYUBRR);
20
  
21
  at_send("ate0", "OK");
22
  at_send("at", "OK");
23
  
24
  while(at_send("at+cpin?", "ERROR"))
25
  {
26
    //display message that no sim is inserted
27
  }
28
  
29
  if(at_send("at+cpin?", "+CPIN: SIM PIN"))
30
  {
31
    at_send("at+cpin=XXXX", "OK");
32
  }
33
  _delay_ms(1000);
34
  at_send("at", "OK");
35
  at_send("at", "OK");
36
  at_send("at", "OK");
37
}
38
39
void uart_get_str(char *s)
40
{
41
  unsigned char c;
42
  
43
  while(count_lf != 2)
44
  {
45
    c = uart_getc();
46
    if(c == 10)
47
    {
48
      count_lf++;
49
    }
50
    if(c != 10 && c != 13)
51
    {
52
      *s = c;
53
      s++;
54
    }
55
  }
56
  *s = 0;
57
  count_lf = 0;
58
}
59
60
unsigned char at_send(char *cmd, char *a)
61
{
62
  uart_str(cmd);
63
  uart_c(13);
64
  clear_answer();
65
  uart_get_str(&answer);
66
  if(strcmp(answer, a) == 0)
67
  {
68
    led_ok();
69
    return 1;
70
  }
71
  else
72
  {
73
    led_error();
74
    return 0;
75
  }
76
}
77
78
void clear_answer(void)
79
{
80
  strcpy(answer, "");
81
}

Also das ganze funktioniert einwandfrei bis ich das erstemal das AT 
Commando AT+CPIN verwende.. ab dem Zeitpunkt gibt die Funktion at_send() 
nur noch 0 wieder.

Wo liegt mein Fehler?

von Karl H. (kbuchegg)


Lesenswert?

f95 schrieb:

> unsigned char at_send(char *cmd, char *a)
> {
>   uart_str(cmd);
>   uart_c(13);
>   clear_answer();
>   uart_get_str(&answer);

Da würde ich mir doch glatt mal ausgeben lassen, welche Antwort das 
Modem zurückschickt und den Text einfach mal irgendwo ausgeben lassen.

Es ist nie gut, wenn man 'Programmieren mittels Stochern im Nebel' 
betreibt. Das erste was man praktisch immer braucht, ist eine 
Ausgabemöglichkeit, damit man sich auch mal irgendwelche 
Zwischenergebnisse ansehen kann.


Das du hier
  uart_get_str(&answer);
allerdings die Adresse von answer übergibst, macht mich stutzig. Wie ist 
den answer definiert?

von f95 (Gast)


Lesenswert?

Ich programmiere schon sehr kleinschrittig. Wenn ich jeden Befehl 
einzelnt nach einander ins Modem lade, geht es. Sobald ich aber eine 
Pinabfrage mit einer anderen kombiniere, funktioniert es nciht mehr. Und 
ab irgend einem Punkt muss man etwas größere Schritte in der Entwicklung 
machen. Ich habe das ganze jetzt mal Simuliert mitm Terminal und da 
funktioniert es. Aber ich werde mir das ganze nochmal ausgeben lassen. 
Danke

von f95 (Gast)


Lesenswert?

Hier ist die Definition meiner globalen Variabeln:
1
//global variables--------------------------------------------------
2
unsigned char count_lf = 0;
3
char answer[100] = "";

von Karl H. (kbuchegg)


Lesenswert?

f95 schrieb:
> Hier ist die Definition meiner globalen Variabeln:
>
>
1
> //global variables--------------------------------------------------
2
> unsigned char count_lf = 0;
3
> char answer[100] = "";
4
>


count_lf sollte eigentlich lokale Variable in der Funktion sein. (UNd 
dann besser uint8_t und nicht char)

answer ist ok. Nimm aber dann den & weg.

   uart_get_str(answer);

von Karl H. (kbuchegg)


Lesenswert?

f95 schrieb:
> Ich habe das ganze jetzt mal Simuliert mitm Terminal und da
> funktioniert es.

Das ist grundsätzlich nicht schlecht ...

> Aber ich werde mir das ganze nochmal ausgeben lassen.

... das ist aber besser.

von f95 (Gast)


Lesenswert?

Hi also ich hab die uart_get_str methode umgeschrieben sodass es mir 
eigentlich den String den es bekommt noch einmal ausgibt. Doch es kommt 
nur das Linefeed welches dadrunter ist.
1
void uart_get_str(char *s)
2
{
3
  unsigned char c;
4
  
5
  while(count_lf != 2)
6
  {
7
    c = uart_getc();
8
    if(c == 10)
9
    {
10
      count_lf++;
11
    }
12
    if(c != 10 && c != 13)
13
    {
14
      *s = c;
15
      s++;
16
    }
17
  }
18
  *s = 0;
19
  count_lf = 0;
20
  uart_str(answer);
21
  uart_c(10);
22
}

und es funktioniert auch nicht, wenn ich mir s oder *s ausgeben lasse

von f95 (Gast)


Lesenswert?

Habe die Ausgabe in eine andere Funktion gesetzt und die Strings die 
miteinander vergleichen werden, sind identisch. Habe mir die Hexwerte 
angesehen. Rückgabewerte auch korrekt in der Simmulation.. Doch sobald 
UART und Modem zusammen sind funktioniert es nicht mehr.

von Karl H. (kbuchegg)


Lesenswert?

f95 schrieb:
> Habe die Ausgabe in eine andere Funktion gesetzt und die Strings die
> miteinander vergleichen werden, sind identisch. Habe mir die Hexwerte
> angesehen. Rückgabewerte auch korrekt in der Simmulation..

AB sofort kannst du die Simulation vergessen.
Deine get_str Funktion kommt mit den CR LF #so wie sie das Modem 
schickt# durcheinander.

Das musst du erst mal richtig stellen.

von f95 (Gast)


Lesenswert?

Wieso kommen die durcheinander durch die if abfrage werden die doch 
einfach ignoriert. Und bevor ich sie ignoriere, wird doch immer geguckt 
ob ein LF kommt denn damit beginnt eine Antwort und endet auch wieder.

Wie z.B. <CR><LF>OK<CR><LF>

Also eine andere Methode an den String zu kommen den ich abfragen kann 
ist mir nicht gekommen als LF zu zählen und beide für die Stringbildung 
zu überspringen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Die CRLF vor dem OK kommen von dem Echo, was eingeschaltet ist. Du 
solltest besser erstmal das echo mit ATE0 abschalten, dann bekommst Du 
ein reines

      OK<CR><LF>

als Antwort zurück. Das ist wesentlich einfacher auszuwerten als die 
Sache mit der LF-Zählerei.

von f95 (Gast)


Lesenswert?

Mit ate0 schalte ich nur aus, das der Befehl nicht wiederholt wird und 
ich nur ein reines <cr><lf>OK<cr><lf> habe daher ja die Zählerei.

von f95 (Gast)


Lesenswert?

Hier ein Beispiel:

at<\r><\r><\n>OK<\r><\n>
ate0<\r><\r><\n>OK<\r><\n>
<\r><\n>
OK<\r><\n>

habe die Befehle AT ATE0 und AT geschrieben..

von Karl H. (kbuchegg)


Lesenswert?

f95 schrieb:
> Mit ate0 schalte ich nur aus, das der Befehl nicht wiederholt wird und
> ich nur ein reines <cr><lf>OK<cr><lf> habe daher ja die Zählerei.

Das glaub ich dir alles.
Aber erzähl das nicht mir.
Schau dir an, was dein Code tatsächlich für Zeichen bekommt und bestimme 
dann, was da schief geht! Es ist eine Sache, in der Theorie zu wissen, 
wie das ablaufen sollte, es ist aber eine andere Sache, welche Zeichen 
dann tatsächlich daher kommen. Das ist ein Unterschied!

Und wenn das bedeutet, dass du dir hier
1
void uart_get_str(char *s)
2
{
3
  unsigned char c;
4
  
5
  while(count_lf != 2)
6
  {
7
    c = uart_getc();
8
9
***************************
10
11
12
    if(c == 10)
13
    {
14
      count_lf++;
15
    }
16
    if(c != 10 && c != 13)
17
    {
18
      *s = c;
19
      s++;
20
    }
21
  }
22
  *s = 0;
23
  count_lf = 0;
24
  uart_str(answer);
25
  uart_c(10);
26
}
an der markierten Stelle das c (also ASCII und als Hex) mal ausgeben 
lässt, dann bedeutet es das eben. Und wie schon gesagt: die Variable 
count_lf würde ich auf jeden Fall in die Funktion mit reinziehen, damit 
sicher gestellt ist, dass du sie vor dem ganzen Klimbin auf 0 stellen 
kannst und das dann auch stimmt. Sich darauf zu verlassen, dass die 0 
vom return bis zum nächsten Aufruf erhalten bleibt, ist ein Spiel mit 
dem Feuer. Eigentlich sollte es so sein. Aber eigentlich sollte in einem 
C-Programm auch kein Buffer Overflow stattfinden, der andere Variablen 
niederbügelt.
1
void uart_get_str(char *s)
2
{
3
  unsigned char count_lf = 0;
4
  unsigned char c;
5
  
6
  while(count_lf != 2)
7
  {
8
    c = uart_getc();
9
10
***************************
11
12
13
    if(c == 10)
14
    {
15
      count_lf++;
16
    }
17
    if(c != 10 && c != 13)
18
    {
19
      *s = c;
20
      s++;
21
    }
22
  }
23
  *s = 0;
24
  uart_str(answer);
25
  uart_c(10);
26
}

von Karl H. (kbuchegg)


Lesenswert?

Und PS: die Kontrollausgabe auf der UART ist keine so gute Idee. Ein LCD 
wäre besser.
Grund: Für jedes Zeichen, welches du bekommst darfst du nur 1 Zeichen 
(bei gleicher Baudrate) wieder ausgeben, sonst verlierst du Zeichen am 
Input.

Du brauchst eine Ausgabemöglichkeit, mit der du auch ein wenig 
Zwischenwerte ausgeben kannst, ohne dass du dir gleich das komplette 
Timing versaust.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Schau dir an, was dein Code tatsächlich für Zeichen bekommt

UND ZWAR VOM MODEM !

Irgendwelche SImulationen oder wenn du dich vor ein Terminal klemmst und 
Modem spielst bringen dich jetzt nicht mehr weiter. Du musst mit dem 
Modem selber arbeiten. Es bestimmt jetzt, wie es weitergeht.

von f95 (Gast)


Lesenswert?

Da der LCD auf dem Weg ist Habe ich nur 2 LEDs und stöpsel ständig 
zwischen Modem und allem hin und her, um die Ausgabe ans Modem zu 
überprüfen. Gibt es denn keienn Trick wie man so eine Verbindung 
spiegeln kann, sodass ich mir den Datenfluss im Terminal angucken kann?

Erstmal Danke für eure Hilfe ich werde gucken, das ich die get_str 
Funktion abändern werde das sie stabieler läuft.

Bin offen für Tipps :)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

f95 schrieb:
> Hier ein Beispiel:
>
> at<\r><\r><\n>OK<\r><\n>
> ate0<\r><\r><\n>OK<\r><\n>
> <\r><\n>
> OK<\r><\n>

Ich pflücke das mal auseinander:

1. Zeile:

at\r    das ist das Echo des Modems auf Dein at<\r>
\r\n    das Modem schickt nun einen Zeilenumbruch CRLF auf Deine Eingabe
OK\r\n  das Modem sendet OK und CRLF

Das ist alles normal.

Auch die zweite Zeile ist ganz normal. Aber mich hätte interessiert, wie
ein AT\r DANACH ausgesehen hätte, also NACHDEM das Echo 
ausgeschaltet wird - nicht vorher. Denn dann hättest Du nur noch 
empfangen dürfen:

OK\r\n
OK\r\n
OK\r\n
... usw.

Leider hast Du den spannenden Teil weggelassen.

Gruß,

Frank

EDIT:
Ich würde das Modem an eine Terminalemulation anschließen und selbst 
dauerhaft auf ohne Echo umprogrammieren, also:

ATE0          Echo aus
AT&W          Konfiguration abspeichern (Vorsicht: Modem-Typ-Relevant!)

von Stefan W. (dl6dx)


Lesenswert?

f95 schrieb:
> Gibt es denn keinen Trick wie man so eine Verbindung
> spiegeln kann, sodass ich mir den Datenfluss im Terminal angucken kann?

Häng dich an die Leitungen zum Modem.

Entweder vollduplex, dann brauchst du zwei weitere serielle Ports.

Oder nutze den Umstand, dass die Kommunikation halbduplex abläuft und 
schalte die beiden Datenleitungen über ein Dioden-Oder zusammen.

Zur Grundidee des Dioden-Oder siehe 
http://www.serialmon.com/cables/3.html

Grüße

Stefan

von f95 (Gast)


Lesenswert?

Warum habe ich das spannende weggelassen. Ich wiederhole es nochmal:

Modem start
->at<\r>

Antwort:
at<\r><\r><\n>OK<\r><\n>

Echo ausschalten:
->ate0<\r>

Antwort:
ate0<\r><\r><\n>OK<\r><\n>

Weiterer AT Befehl:
->at<\r>

Antwort:
<\r><\n>
OK<\r><\n>

Hier nochmal meine Logs:
Received Data:

at<\r><\r><\n>OK<\r><\n>
ate0<\r><\r><\n>OK<\r><\n>
<\r><\n>
OK<\r><\n>

Transmitted Data:
at<\r>ate0<\r>at<\r>

von f95 (Gast)


Lesenswert?

und Danke für den Tipp mit den Dioden doch das lässt sich leider hier 
nicht verbauen.

Also ich habe jetzt noch ein wenig weiter rumgespielt und mir die 
letzten ampfangenen befehl auf Knopfdruck, also wenn die UART umgesteckt 
wurde auf das Terminal. Dann ist die Antwort nachdem ich den Befehl 
at+cpin benutzt habe.

Es liegt wahrscheinlich an meiner Zählmethode nur fällt mir gerade keine 
andere Möglichkeit ein den Text zwischen <CR><LF>TEXT<CR><LF> auszulesen 
außer die Zeichen zu ignorieren und die letzten Zeichen sprich das LF zu 
zählen.

von Karl H. (kbuchegg)


Lesenswert?

f95 schrieb:

> Modem start
> ->at<\r>
>
> Antwort:
> at<\r><\r><\n>OK<\r><\n>

Das ist das was du im Terminal siehst.
Tatsächlich ist aber die zeitliche Abfolge höchst wahrscheinlich ein 
wenig anders.

Du tippst 'a' und das Modem antwortet sofort mit dem Echo 'a'. Du tippst 
't' und das Modem antwortet sofort mit einem 't'. Du tippst Enter (also 
\r) und das Modem antwortet sofort mit einem \r.

Bis hier her ist das nicht die 'Antwort' sondern das Echo.

Dann bearbeitet das Modem die Zeile und erst jetzt antwortet es

\r\nOK\r\n


Jetzt kommt der knifflige Punkt. Je nachdem wie schnell dein Programm 
ist und wie lange das Modem zur Bearbeitung des Kommandos braucht, 
kriegt es vom Echo noch was mit oder eben auch nicht. Das kann zb ein \r 
(vom Echo) zuviel sein.

Wie kann man das Problem lösen, dass man nicht weiß, was man genau 
kriegt. Eine Möglichkeit ist zb: Echo dauerhaft abschalten. Das ist 
unumgänglich. Dein Programm braucht das Echo nicht, es verkompliziert 
nur alles, weil das Programm dann eigentlich das Echo ausfiltern muss. 
Echo ist gut, wenn ich mich mit einem Terminal an ein Gerät klemme, 
damit ich beim Tippen sehe was ich tippe. Echo ist auch gut, damit ich 
im Terminal sofort sehe ob die Baudrate stimmt oder ob es da ein Problem 
gibt. Aber für ein Programm ist es die Hölle vom Gerät ein Echo zu 
bekommen, das ausgefiltert werden muss. Das legt dir ein sehr enges 
Korsett an, wie du das Timing deines Programmes gestalten musst. Denn: 
Du darfst vom Echo kein einziges Byte verpassen. Du musst jederzeit in 
der Lage sein Echo und Antwort auseinander zu halten.

Zum anderen wissen wir, dass das Modem NIE mit einer leeren Zeile 
antwortet. Die Antwort auf irgendein AT-Kommando ist immer eine 
nichtleere Zeile. Ist also ein \r\n empfangen worden UND ist dabei eine 
leere Zeile entstanden, dann war dieser \r\n noch nicht der letzte 
sondern da kommt noch was. Nämlich die Zeile in der dann die richtige 
Antwort steht. Dadurch dass du das erste \r vom Echo fälschlicherweise 
mitgezählt hast, ist alles durcheinander gekommen.

von Stefan W. (dl6dx)


Lesenswert?

f95 schrieb:
> nur fällt mir gerade keine andere Möglichkeit ein den Text zwischen
> <CR><LF>TEXT<CR><LF> auszulesen außer die Zeichen zu ignorieren und
> die letzten Zeichen sprich das LF zu zählen

Bau dir doch eine finite state machine (FSM) für die Analyse des 
Zeichenstroms.

Grüße

Stefan

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

f95 schrieb:

> Weiterer AT Befehl:
> ->at<\r>
>
> Antwort:
> <\r><\n>
> OK<\r><\n>

Okay, jetzt, wo das Echo abgeschaltet ist, siehst Du eine Leerzeile vor 
dem OK.

Dann könnte nun die Empfangsroutine folgendermaßen aussehen:
1
void uart_get_str(char *s)
2
{
3
  unsigned char len = 0;
4
  unsigned char c;
5
  
6
  while (1)
7
  {
8
    c = uart_getc();
9
10
    if (c == '\n')
11
    {
12
      if (len > 0)
13
      {
14
         break;
15
      }
16
    }
17
    else if (c != '\r')
18
    {
19
      *s++ = c;
20
      len++;
21
    }
22
  }
23
  *s = 0;
24
  uart_str(answer);
25
  uart_c('\n');
26
}

Diese Funktion ignoriert Leerzeilen, indem sie die Anzahl der zu 
speichernden Zeichen mitzählt. Finde ich einfacher und sicherer, als 
Zeilenenden zählen zu wollen.

von f95 (Gast)


Lesenswert?

Danke hat super funktioniert. Habe zwar jetzt ein paar Attefakte auf der 
Leitung. Ab und zu sendet er das letzte empfangene Zeichen aber das hat 
keinen Einfluss auf das Modem. Da sehe ich eher das fehlende Quarz als 
Problem.

Nochmals vielen Dank

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.