Forum: Mikrocontroller und Digitale Elektronik UART String einlesen


von schurli (Gast)


Lesenswert?

Hi

Kann mir jemand sagen was für Flags gesetzt werden, wenn auf der 
seriellen Schnittstelle Daten daherkommen?

Ich möchte in einer while(1) schleife Strings einlesen. (Aber nur dann 
einlesen wenn welche daherkommen).

Kann mir jemand ein Teilfragment vom einlesen eines Strings posten?

mfg

von Matthias L. (Gast)


Lesenswert?

>Kann mir jemand ein Teilfragment vom einlesen eines Strings posten?

Siehe Datenblatt, Kapitel U(S)ART- Data Reception

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Um welchen µC geht es?

von schurli (Gast)


Lesenswert?

Atmega 64.

Hier meine Problembeschreibung:

Ich möchte mit dem Atmega64 SMS lesen und versenden können.
Dafür würde ich ja wie immer uart_gets() benutzen 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 die uart_gets()
funktion nicht um mit dem programm fortzufahren.

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 Florian P. (db1pf)


Lesenswert?

Hallo,

schau mal ins Datenblatt vom ATMega64. In einem UART-Register UCSRA 
gibt's ein Flag, das heißt RXC. Es wird auf 1 gesetzt, wenn ein Zeichen 
empfangen wurde.
Es reicht also wenn du vor dem Aufruf von uart_gets() überprüfst ob 
dieses Bit gesetzt ist. Wenn nein kannst den PinX abfragen, oder 
sonstige Aktionen durchführen.

Gruß, Florian

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Wenn du mit
>uart_gets()
nichts anfangen kannst, dann ist das die falsche Funktion für dich.

In eine Hauptprogrammschleife vom Typ
while(1){}
gehört nichts, was die Ausführung der Schleife grundsätzlich verhindern 
kann.

Mein Tipp:
1) sieh nach, ob ein Zeichen gekommen ist z.B. über uart_getc()
2) falls nein: mach weiter mit der Schleife 4)
3) falls ja: füge es zu deinem String dazu,
   ist es ein CR oder LF dann ersetze es durch \0 und
   werte dann den so erhaltenen String aus
4) Tu den Rest der Schleife (Pinabfrage....)
5) Weiter bei 1)

von schurli (Gast)


Lesenswert?

@Florian Pfanner

Werde ich später ausprobieren!
Wird aber warscheinlich zur Lösung führen.
Danke!

von schurli (Gast)


Lesenswert?

Kann mir jemand sagen wo da der Fehler liegen könnte.
Der Atmega bleibt in der While(1) schleife hängen.

1
unsigned char neu[10];
2
3
while (1)
4
{
5
  if((UCSR0A & (1<<RXC)))
6
  {
7
   printf("Hallo\n");   //Gibt Hallo an die Serielle Schnittstelle aus.
8
   gets(neu);           //Speichert den String auf neu ab.
9
  }
10
}

Wie gesagt will ich einen String einlesen, sobald ich was auf der 
seriellen Schnittstelle empfang, sonst soll das Programm andere 
Tätigkeiten ablaufen lassen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ist printf schon auf STDOUT gerichtet und gets auf STDIN?

Siehe http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html

"The standard streams stdin, stdout, and stderr are provided, but 
contrary to the C standard, since avr-libc has no knowledge about 
applicable devices, these streams are not already pre-initialized at 
application startup..."

Und wie man die Zuordnung mit Hilfe von FDEV_SETUP_STREAM  macht 
(http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gea2b6be92ead4673bc487b271b7227fb 
und Democode in 
http://www.nongnu.org/avr-libc/user-manual/group__stdiodemo.html)

EDIT: Siehe auch 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf

von schurli (Gast)


Lesenswert?

ist drauf gerichtet und funtkioniert auch, aber irgenwas hats mit der 
abfrage von der seriellen schnittstelle glaube ich.

von Michael H* (Gast)


Lesenswert?

schurli wrote:
> Kann mir jemand sagen wo da der Fehler liegen könnte.
> Der Atmega bleibt in der While(1) schleife hängen.
warum eigentlich nicht über den RXC interrupt?

>
1
> unsigned char neu[10];
2
> 
3
> while (1)
4
> {
5
>   if((UCSR0A & (1<<RXC)))
                       ^^^ RXC0 muss es heißen
1
>   {
2
>    printf("Hallo\n");   //Gibt Hallo an die Serielle Schnittstelle aus.
3
>    gets(neu);           //Speichert den String auf neu ab.
4
>   }
5
> }
6
>
>
> Wie gesagt will ich einen String einlesen, sobald ich was auf der
> seriellen Schnittstelle empfang, sonst soll das Programm andere
> Tätigkeiten ablaufen lassen.
klingt wie gemacht für interrupts

von schurli (Gast)


Lesenswert?

1
//Installation der Seriellen Schnittstelle
2
void Handy_Init (void){
3
4
  //Enable TXEN im Register UCR TX-Data Enable
5
  UCR=(1 << TXEN | 1<<RXEN);
6
  //Teiler wird gesetzt 
7
  UBRR=(SYSCLK / (BAUD_RATE * 16L) - 1);
8
  
9
  //öffnet einen Kanal für printf (STDOUT)
10
  fdevopen (uart_putchar, uart_getchar);
11
};
12
13
14
//Routine für die Serielle Ausgabe
15
int uart_putchar (char c){
16
17
  if (c == '\n')
18
    uart_putchar('\r');
19
  //Warten solange bis Zeichen gesendet wurde
20
  while(!(USR & (1<<UDRE)));
21
  //Ausgabe des Zeichens
22
  UDR = c;
23
  return (0);
24
};
25
26
//Routine für die Serielle Ausgabe
27
int uart_getchar (void){
28
29
  while(!(USR & (1<<RXC)));
30
  return(UDR);
31
};


Hier der Codeausschnitt.

Wie kann man es über den RXC interrupt machen?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

schurli wrote:
> ist drauf gerichtet und funtkioniert auch, aber irgenwas hats mit der
> abfrage von der seriellen schnittstelle glaube ich.

Dan erläutere bitte, was genau du mit "bleibt in der While(1) schleife 
hängen" meinst.

Im Moment besteht dein Codeschnippsel nämlich aus einer Endlosschleife; 
das Programm MUSS in der while(1) Schleife bleiben.

Wenn du andere Tätigkeiten abzuarbeiten hast, könntest du die hier 
unterbringen:

unsigned char neu[10];

while (1)
{

  // Auf UART-Eingabe prüfen
  if((UCSR0A & (1<<RXC)))
  {
    printf("Hallo\n");   //Gibt Hallo an die Serielle Schnittstelle aus.
    gets(neu);           //Speichert den String auf neu ab.
  }

  // ggf. Eingabe auswerten und
  // sonstigen Code ausführen

}

von schurli (Gast)


Lesenswert?

gets(neu) bringt den MC zum absturz?

von schurli (Gast)


Lesenswert?

... obwohl ich weniger als 10 zeichen eingebe und Enter drücke

von Michael H* (Gast)


Lesenswert?

deine register stimmen nicht. die routinen wurden wohl für einen andren 
µc geschrieben. lies dir einfach mal das datenblatt und das tutorial 
hier jeweils zum thema uart durch

von schurli (Gast)


Lesenswert?

Sorry hab vergessen das die mit dem Präprozessor definiert wurden.
1
#if defined (__AVR_ATmega64__)
2
#  define USR UCSR0A
3
#  define UCR UCSR0B
4
#  define UDR UDR0
5
#  define UBRR UBRR0L
6
#  define EICR EICRB
7
#endif

von Michael H* (Gast)


Lesenswert?

ahja. dann stimmt aber trotzdem RXC noch nicht. muss RXC0 heißen. die 
sind nicht an der gleichen stelle bei anderen typen.

von Michael H* (Gast)


Lesenswert?

und noch hinterher:

> int uart_putchar (char c){
warum denn int? void tuts doch.
>  if (c == '\n')
>    uart_putchar('\r');
rekursion?
> ...
> };

von schurli (Gast)


Lesenswert?

Hat zwar schon anstandslos mit RXC statt RXC0 funktioniert obwohl es 
definitiv falsch ist.

Trotzdem hängt das Programm beim Einlesen des Strings.
"Hallo" wird mir auf der Seriellen ausgegeben, aber dann steht alles.

von schurli (Gast)


Lesenswert?

Die Funtkion stammt von Ulrich Radig und ist auf Siemens Handys 
zugeschnitten.

von schurli (Gast)


Lesenswert?

1
unsigned char neu;
2
3
while (1)
4
{
5
  if((UCSR0A & (1<<RXC0)))
6
  {
7
   printf("Hallo\n");   //Gibt Hallo an die Serielle Schnittstelle aus.
8
   neu=uart_getchar();          
9
  }
10
}

Komischerweise funktioniert das einlesen eines Zeichens wunderbar.
Wenn ich im Hyperterminal irgenwas eingebe, schickt mir der AVR "Hallo" 
zurück und das Programm läuft in der while(1) weiter.

10mal irgendwas drücken ergibt 10mal "Hallo" also funktiniert es.

Warum funktioniert gets() nicht?

mfg

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Welches Zeichen ist den das Zeilenendezeichen in gets? Tipp, dies ist 
der Code von gets() aus avr-libc 1,6.2:
1
#include <stdio.h>
2
3
#include "stdio_private.h"
4
5
char *
6
gets(char *str)
7
{
8
  char *cp;
9
  int c;
10
11
  if ((stdin->flags & __SRD) == 0)
12
    return NULL;
13
14
  for (c = 0, cp = str; c != '\n'; cp++) {
15
    if ((c = getchar()) == EOF)
16
      return NULL;
17
    *cp = (char)c;
18
  }
19
  *--cp = '\0';
20
21
  return str;
22
}

Und welches Zeichen schickt dein UART-Sender (Terminalprogramm?, Handy?) 
am Ende einer Zeile?

von schurli (Gast)


Lesenswert?

Hi

Danke funktioniert.
"\n" wird gesendet.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Stefan "stefb" B. wrote:

> Wenn du andere Tätigkeiten abzuarbeiten hast, könntest du die hier
> unterbringen:
>
> unsigned char neu[10];
>
> while (1)
> {
>
>   // Auf UART-Eingabe prüfen
>   if((UCSR0A & (1<<RXC)))
>   {
>     printf("Hallo\n");   //Gibt Hallo an die Serielle Schnittstelle aus.
>     gets(neu);           //Speichert den String auf neu ab.
>   }
>
>   // ggf. Eingabe auswerten und
>   // sonstigen Code ausführen
>
> }

Das setzt allerdings voraus, dass der restliche Code nicht zu viel Zeit 
braucht (abh. von F_CPU und BAUDRATE). Der Hardware-UART-Puffer ist 
derzeit nur 2 Zeichen gross und es besteht grosse Gefahr, dass Zeichen 
verloren gehen.

Ein grösserer Empfangspuffer und eine vom Userprogramm unabhängig 
laufende RX-Interruptroutine wären sicher die geschicktere Lösung.

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.