Forum: Mikrocontroller und Digitale Elektronik Keine vollst. Übertragung der Strings über UART


von Louis (Gast)


Lesenswert?

Hallo,

ich möchte gerne serielle Daten mit Hilfe meines Atmega 2560 
kontrollieren. Dabei sendet mein Gerät verschiedene Strings, die durch 
jeweilige Zeichen am Anfang der Zeichenketten gekennzeichnet sind- 0x04 
und 0x08.

Ich möchte nur die Strings, die mit dem Terminator 0x08 beginnen 
untersuchen. Diese sind 66 Zeichen lang. Ich habe mich nun  mal daran 
gemacht, die vom Gerät gesendeten Daten einfach an meinen PC bzw. 
Hyperterminal weiterzusenden. Allerdings happert’s hierbei leider schon.

Mit dem unten angefügten Programm, kann ich nach der Sendeaufforderung 
der seriellen Daten, zwar den entsprechenden Terminator, also 0x08, 
detektieren, allerdings erhalte ich nur jeden zweiten String in seiner 
vollen Länge, sprich die 66 Zeichen (0x08 + 64-Zeichen + 0x0D).
Dazwischen erhalte ich nur zwei bis drei Zeichen des seriellen 
Protokolls.

Mir scheint es, dass die Daten einfach zu schnell „reinkommen“ und das 
Programm keine Zeit hat, diese zu verarbeiten. Es kann durchaus sein, 
das 120 Strings mit dem Terminator 0x08 nacheinander folgen.

Ich habe noch nicht allzu viel Erfahrung und würde mich freuen, wenn mir 
jemand weiterhelfen könnte.

VG,
Louis

#include <avr/io.h>
#include <stdio.h>
#include <string.h>

#define FOSC 16000000
#define BAUD 9600
#define UBRR_VAL (FOSC/16/BAUD-1)

void USART_INIT (unsigned int UBRR)
{
UBRR0H = (unsigned char)(UBRR>>8);
UBRR0L = (unsigned char)(UBRR);
UCSR0B  =(1 << RXEN0) | (1 << TXEN0);
UCSR0C  =(1<< UCSZ01) | (1 << UCSZ00);
}
uint8_t GET_CHAR(void){
while (!(UCSR0A & (1<<RXC0)));
return UDR0;
}

uint8_t GET_STRING(char *BUFFER, int MAX_LENGTH){
char NEXT_CHAR;
int STRING_LENGTH = 0;

NEXT_CHAR = GET_CHAR();

while( (NEXT_CHAR != 0x0D) && (STRING_LENGTH < MAX_LENGTH - 1)){

*BUFFER++ = NEXT_CHAR;
STRING_LENGTH++;
NEXT_CHAR = GET_CHAR();
}
*BUFFER = '\0';
}


void SEND_CHAR(unsigned char data){
while (!(UCSR0A & (1 << UDRE0)));
UDR0 = data;
}

void SEND_STRING(char *STRING){
while(*STRING){
SEND_CHAR(*STRING);
*STRING++;
}
SEND_CHAR( 0x0D );
}

void SEND_DATA(void){
SEND_CHAR( 0xAB );
SEND_STRING("4555BCV");
}


void main(void)
{
char TEMP[66];
char LOG_TEMP[66];

USART_INIT(UBRR_VAL);

SEND_DATA();

while(1){

GET_STRING(TEMP,66);
if(TEMP[0] == 0x08){
SEND_STRING(TEMP);
}
}
}

von g457 (Gast)


Lesenswert?

> Mir scheint es, dass die Daten einfach zu schnell „reinkommen“ und das
> Programm keine Zeit hat, diese zu verarbeiten. Es kann durchaus sein,
           ^^................................^^
jepp

> das 120 Strings mit dem Terminator 0x08 nacheinander folgen.
                                          ^^...............^^

Rechne mal kurz nach wie lange es dauert um einen String der länge n zu 
übertragen.. und dann rechne nach wie lange es dauern ∗dürfte∗, damit 
der Anfang der nächsten (unmittelbar anschließend eingehenden) 
Übertragung ∗nicht∗ verloren geht. Genau, da liegt ein Faktor von (etwas 
mehr als) n dazwischen. Deswegen funktioniert dasda

> GET_STRING(TEMP,66);
> if(TEMP[0] == 0x08){
>    SEND_STRING(TEMP);

so gar nicht (richtig).

HTH

von !G (Gast)


Lesenswert?

Den Code bitte vernünftig formatieren und in [ c] [ /c]-Tags einfassen.

Üblicherweise werden Funktionen und Variablen nicht komplett groß 
geschrieben, das wird nur mit Präprozessormakros usw. gemacht.

von Louis (Gast)


Lesenswert?

Hi,
danke für die schnelle Antwort! Das mit der Formatierung werde ich mir 
merken.
Hmmmm, wie kann ich das dann am besten umgehen, bzw. welchen Ansatz soll 
ich hierbei am besten verwenden?
VG,
Louis

von g457 (Gast)


Lesenswert?

> Hmmmm, wie kann ich das dann am besten umgehen, bzw. welchen Ansatz soll
> ich hierbei am besten verwenden?

Interruptgesteuert, ggf. zwei (echt unterschiedliche) Puffer. Dann 
schreibt der Empfänger(-Interrupt) in den einen Puffer, wenn der voll 
ist (und als aufhebenswert erachtet wurde), dann werden die Puffer 
getauscht (Achtung Nebenläufigkeit!), der Empfänger(-Interrupt) schreibt 
in den 'neuen' Puffer, der Sender(-Interrupt) überträgt zeitgleich den 
'alten'.

Mit etwas Geschickt kann mans auch in einen einzigen Puffer 
reinquetschen, der nur zur Sicherheit einen kleinen Überhang mitbring. 
Vom Prinzip ein Ringpuffer der von Empfänger(-Interrupt) vollgeschrieben 
und vom Sender(-Interrupt) geleert wird.

HTH

von Louis (Gast)


Lesenswert?

Hallo zusammen,
ich hab nun mal den Rat von "g457" befolgt und mich an ein 
interrupt-gesteuertes Empfangen und Senden der Daten gemacht. Allerdings 
erhalte ich nur "Nonsense" übers Hyperterminal. Immer nur ein Zeichen 
wird permanent gesendet. Ich hab mal die Tutorials verwendet und 
versucht den Fehler aufzuspüren. Ohne Erfolg...Ich würde mich freuen, 
wenn mir jemand auf die Sprünge helfen könnte. VG, Louis

#include <avr/io.h>
#include <stdio.h>
#include <string.h>
#include <avr/interrupt.h>

#define FOSC 16000000
#define BAUD 9600
#define UBRR_VAL (FOSC/16/BAUD-1)
#define UART_BUFFER_SIZE 117

volatile uint8_t UART_RX_FLAG = 0;  // String komplett
volatile uint8_t UART_TX_FLAG = 1;  // String komplett
volatile uint8_t UART_TX_COUNTER = 0;
volatile uint8_t UART_RX_COUNTER = 0;
volatile char UART_RX_BUFFER[UART_BUFFER_SIZE];
volatile char UART_TX_BUFFER[UART_BUFFER_SIZE];

void USART_INIT (unsigned int UBRR)
{
UBRR0H = (unsigned char)(UBRR>>8);
UBRR0L = (unsigned char)(UBRR);
UCSR0B  =(1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0) | (1 << TXCIE0);
UCSR0C  =(1<< UCSZ01) | (1 << UCSZ00);
}

ISR(USART0_RX_vect){

unsigned char DATA;

DATA = UDR0;

if(UART_RX_FLAG == 0)
{
if( DATA == 0x0D ){            UART_RX_BUFFER[UART_RX_COUNTER] = 0;
UART_RX_FLAG = 1;
UART_RX_COUNTER = 0;
}

else if( (DATA != 0x0D) && (UART_RX_COUNTER < (UART_BUFFER_SIZE - 1)) ){
UART_RX_BUFFER[UART_RX_COUNTER] = DATA;
UART_RX_COUNTER++;
}
}
}

ISR(USART0_UDRE_vect){

unsigned char DATA;
char *UART_TX_POINTER = UART_TX_BUFFER;

DATA = *UART_TX_POINTER++;
if (DATA == 0x0D){
UCSR0B &=~ (1<<UDRIE0);
UART_TX_POINTER = UART_TX_BUFFER;
UART_TX_FLAG = 1;
}
else
UDR0 = DATA;

}

void SEND_STRING(char *DATEN){
if(UART_TX_FLAG == 1){
strcpy (UART_TX_BUFFER, DATEN);
UART_TX_FLAG = 0;
UCSR0B |= (1<<UDRIE0);
}
}

void GET_STRING(char *DATEN){
if(UART_RX_FLAG == 1){
strcpy (DATEN, UART_RX_BUFFER);
UART_RX_FLAG = 0;
}
}

void main(void)
{
char STRING_BUFFER[117];
uint8_t BUFFER_FULL = 0;

USART_INIT(UBRR_VAL);

sei();

while(1){

if((UART_RX_FLAG == 1) && (BUFFER_FULL == 0))
{
GET_STRING(STRING_BUFFER);
BUFFER_FULL = 1;
cli();
}

if((UART_TX_FLAG == 1) && (BUFFER_FULL == 1))
{
SEND_STRING(STRING_BUFFER);
BUFFER_FULL = 0;
sei();
}


}
}

von Bernd (Gast)


Lesenswert?

cli() und sei() müssen in der Hauptschleife entfernt werden...

von Louis (Gast)


Lesenswert?

Hallo Bernd,
danke für die schnelle Antwort. Ich habs soeben probiert, leider ohne 
Erfolg!
VG,
Louis

von Bernd (Gast)


Lesenswert?

Der Code scheint mir aber ansonsten vernünftig zu sein...

von Stefan E. (sternst)


Lesenswert?

UART_TX_POINTER wird bei jedem Interrupt wieder mit UART_TX_BUFFER 
initialisiert, deshalb wird immer das selbe Zeichen gesendet. Da fehlt 
ein "static".

1
UCSR0B  = ... (1 << TXCIE0);
Wenn der erste String komplett gesendet wurde und nicht sofort neue 
Daten zum versenden anstehen, wird dein Programm resetten.

PS: Und BITTE benutze endlich die C-Code-Tags hier im Forum.

von Louis (Gast)


Lesenswert?

Hallo Stefan,
vielen Danke für die Antwort.
Ich habs mal abgeändert, aber es will immer noch nicht. Zum Haare 
raufen!!!
Liegt das vielleicht an meinen Countern, hab ich da was falsch 
gemacht???

von g457 (Gast)


Angehängte Dateien:

Lesenswert?

..ich bin mal so frei, das kann ja keiner lesen..

von Stefan E. (sternst)


Lesenswert?

Louis schrieb:
> Ich habs mal abgeändert, aber es will immer noch nicht. Zum Haare
> raufen!!!

Und wie der Code jetzt mit den Änderungen aussieht, sollen wir raten, 
oder wie?

Und soll "will immer noch nicht" bedeuten, dass das Fehlerbild gleich 
geblieben ist, oder ist es jetzt was Anderes?

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

Oh sorry!
Also ich empfange immer noch nicht die gewünschten Strings. Es werden 
immer nur einzelne Zeichen gesendet (0x00). Der Counter muss ja auch 
static sein.

Das habe ich auch mal abgeändert. Beim durchschauen sind mir jetzt keine 
Fehler mehr aufgefallen, wobei das mit meiner noch etwas dürftigen 
Erfahrung im Programmieren nichts heißen soll. Nochmals Danke für die 
bisherige Hilfe!

Grüße,
Louis

von Lehrmann M. (ubimbo)


Lesenswert?

Servus,

funktioniert denn deine Schaltung richtig und ist die Baudrate richtig 
eingestellt ?

von Stefan E. (sternst)


Lesenswert?

Wenn du im Receive-Interrupt das 0x0D nicht mit in den Buffer schreibst, 
wie soll der Send-Interrupt das dann als Ende-Kennung nutzen können? ;-)

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
zunächst einmal vielen Danke für die Antworten. Klasse das Anfängern 
hier weiter geholfen wird. Ich habs ausprobiert und es funktioniert! 
Allerdings schlag ich mich jetzt die letzten Tage mit einer weiteren 
Sache herum.

Folgendes: ich würde gerne die im Puffer abgelegten Strings, die mit 
0x08 beginnen, auf bestimmte Stellen untersuchen. Beim Auftreten eines 
unerwünschten Zustandes soll eine LED aufleuchten. Wenn 9 bestimmte 
Zustände detektiert wurden, soll ein Befehl ausgesendet werden. 
Irgendwie hege ich den Verdacht, dass das mit meinem Übergeben in den 
Hilfspuffer zusammenhängt.

Allerdings klappt rein gar nichts. Und wenn ich mit SEND_COMMAND 
versuche einen Befehl zu versenden, wird immer nur der Befehl "34ABC9\r" 
über das Hyperterminal gesendet. Ich wäre wirklich für jeden Tipp 
dankbar!
VG,
Louis

von holger (Gast)


Lesenswert?

void SEND_COMMAND( char *STRING )
{
  while( *STRING )
  {
    SEND_CHAR( *STRING );
>    *STRING++;
    STRING++;
  }
  SEND_CHAR( 0x0D );
}

von Karl H. (kbuchegg)


Lesenswert?

Louis schrieb:

> Allerdings klappt rein gar nichts.

Wo rufst du eigentlich deine UART_INIT Funktion auf?

> Und wenn ich mit SEND_COMMAND

Bitte: Hör diesen Unsinn auf, alle Variablen und Funktionsnamen in 
Großbuchstaben zu schreiben.
Ausschliesslich Grossbuchstaben ist in C eine übliche Konvention, die 
ausschliesslich Makros vorbehalten ist. Bei denen ist es auch sinnvoll, 
weil man an der verwendenden Stelle unter Umständen wissen muss, das 
etwas 'nur' ein Makro und keine richtige Funktion ist. Daher kann es 
lebenswichtig sein, im Text zu erkennen, dass etwas ein Makro ist.

Diese Konventionen ist eine der wenigen, an die sich tatsächlich > 98% 
aller C Programmierer weltweit halten. Du hast keine Veranlassung hier 
dein eigenes Süppchen zu kochen, also lass es und benutze dieselbe 
Konvention wie alle anderen auch.

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

Arrggghhhh!!!!
Verflucht, sorry!!!! Mit klappt leider auch nicht...Das mit der 
Großschreibung werde ich mir merken.
VG,

von holger (Gast)


Lesenswert?

>Und wenn ich mit SEND_COMMAND
>versuche einen Befehl zu versenden, wird immer nur der Befehl "34ABC9\r"
>über das Hyperterminal gesendet.

Und genau der Teil in deinem Programm ist auskommentiert.
Ich würde sagen du brennst die falsche HEX-Datei.

von Louis (Gast)


Lesenswert?

Hallo Holger,
das war meinerseits etwas schlecht formuliert. Ich meine, wenn ich den 
Befehl auskommentiere, dann wird permanent "34ABC9\r" gesendet...

von holger (Gast)


Lesenswert?

>Hallo Holger,
>das war meinerseits etwas schlecht formuliert. Ich meine, wenn ich den
>Befehl auskommentiere, dann wird permanent "34ABC9\r" gesendet...

Dann legt dein AVR ne Bauchlandung hin (Reset).
Das könnte daran liegen das du ein SEND_CHAR und
eine Interruptroutine verwendest. Warum eigentlich doppelt?

Du aktivierst TXCIE0 aber dafür gibt es keine Interruptroutine.
USART0_UDRE_vect ist für TXCIE0 nicht zuständig.

von Louis (Gast)


Lesenswert?

Sorry, wie du natürlich schon bemerkt hast, hab ich noch nicht allzu 
viel Erfahrung in Sachen uC-Programmierung. Stimmt, im Grunde lade ich 
ich ja nur neue Daten in das Senderegister. Wie bereits oben erwähnt 
wollte ich das ja alles mittels Polling bewerkstelligen. Allerdings habe 
ich selbst gemerkt, und wie in den Tutorials gelesen, dass das den 
Programmablauf doch recht massiv blockieren kann. Nun gut, dann werde 
ich mich mal an die Arbeit machen.
Dir vielen Dank!

von Reinhard (Gast)


Lesenswert?

Hallo,
Du kannst auch nur mit Hilfe einer Interrupt Routine arbeiten, also 
RXCIE, und mit Hilfe von Send_Char deine Befehle versenden. Wichtig ist 
halt immer, das du im Hauptprogramm ein Flag setzt. Dann kannst du bspw. 
deine Uart-Buffer in einen Hilfs-Buffer entleeren, und dein Programm 
kann somit schneller arbeiten. Gruß

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

Also mit dem Sendeinterrupt komme ich irgendwie nicht ganz zu Recht, ich 
blick da nicht durch. Ich habs jetzt mal anders probiert. Allerdings 
funzt es nicht...Ist von diesem Lösungsansatz abzuraten? Bzw. was hab 
ich verbockt, dass es nicht funktioniert?  Generell passt doch alles!?

von Karl H. (kbuchegg)


Lesenswert?

Louis schrieb:

> ich verbockt, dass es nicht funktioniert?  Generell passt doch alles!?


Geh deinen Code mit dem Finger durch (kein Scherz).
Und dann stell dir die Frage: Wo wird eigentlich jemals GET_CHAR 
aufgerufen?
Also jetzt nicht statisch. Schon klar, dass GET_CHAR aus GET_STRING 
heraus aufgerufen wird. was ich meine ist, wenn du mit dem Finger die 
Programmausführung simulierst, landest du dann jemals in GET_CHAR?

von holger (Gast)


Lesenswert?

if(UART_STRING_COMPLETE == 1)

Wo wird UART_STRING_COMPLETE jemals 1 gesetzt?
Mein Gott, ist das denn so schwer?

von Louis (Gast)


Lesenswert?

Hmmmm,
mein Ziel war es ja eigentlich, den String in get_string in den Buffer 
zu schreiben und dann mit Hilfe von uart_string_complete = 1 dem 
Hauptprogramm zu sagen, das der Buffer in einen Hilfsbuffer kopiert 
werden soll.
Danach setze ich uart_string_complete wieder Null und dadurch lande ich 
wieder in get_string.

Au Backe, ich steh auf dem Schlauch!

Ich möchte ja nicht nur die seriellen Daten auf UART0 untersuchen, 
sondern auch bei Bedarf von UART1. Ich will also mit UART0 die Strings 
einlesen, die ich in der Hauptschleife in einen Buffer kopiere.

Ich möchte aber auch, bspw. nach einer bestimmten Anzahl kopierter 
Strings, die Hauptschleife verlassen und die Strings von UART1 einlesen 
bzw. diese bearbeiten. Ich dachte bisher immer, dass durch das Anlegen 
einer globalen Variablen solche Sprünge problemlos möglich sind.

Oje, trotzdem Danke für die Antworten.
VG,
Louis

von Malte (Gast)


Lesenswert?

Das geht auch. Allerdings solltes du auf den ersten Blick zunächst 
einmal das hier....

DATA = GET_CHAR();

  if(UART_STRING_COMPLETE == 0) {

...vertauschen. Nur nicht gleich die Flinte ins Korn werfen. Grüße und 
noch frohes Schaffen!

von Louis (Gast)


Lesenswert?

Hallo Malte,
Danke für deine Antwort. Allerdings klappts immer noch nicht:-(

>> Nur nicht gleich die Flinte ins Korn werfen

...doch, bald ist es soweit. Grüße

von Louis (Gast)


Lesenswert?

Hallo,
ich weiß das pushen nicht schön ist. Aber wäre vielleicht jemand bereit 
mit weiterzuhelfen? Ich komm nicht drauf...Vielen Dank

von Stefan E. (sternst)


Lesenswert?

Den Hinweis von Karl Heinz hast du wohl nicht ganz ernst genommen, wie? 
Dann also nochmal im Klartext:
In der Hauptschleife wartest du darauf, dass der String komplett ist. 
Wie soll er aber jemals komplett werden, wenn innerhalb dieser Schleife 
niemals GET_STRING aufgerufen wird?

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

Uffff,
da habe ich Interrupt und while völlig vertauscht, vermischt, etc.
Gut, ich muss also mein get_string in die Schleife einbauen. Ich habs 
mal mit einer If-else-Anweisung probiert, funktioniert aber noch nicht. 
Aber das wäre so schon der richtige Weg, oder? Euch vielen Dank für die 
Antworten und Hinweise!
VG,
Louis

von Karl H. (kbuchegg)


Lesenswert?

Du terminierst deinen String nicht richtig

      uart_rx_buffer[uart_rx_counter] = ' ';

Damit ist der String immer noch nicht gültig, weil er kein 
abschliessendes \0 Zeichen hat

> Ich habs mal mit einer If-else-Anweisung probiert

Wozu?
1
  while(1)
2
  {
3
    get_string();
4
            
5
    if(uart_string_complete == 1)
6
    {
7
      strcpy(stringbuffer, uart_rx_buffer);
8
      send_string(stringbuffer);
9
      uart_string_complete = 0;
10
    }
11
  }

machs doch nicht komplizierter als notwendig!
Jetzt musst du nur noch deine get_string richtig stellen.

von Louis (Gast)


Lesenswert?

Hallo Karl-Heinz,
spitze es funktioniert. Danke für deine Hilfe!
VG,
Louis

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.