www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Interruptroutine und Hauptprogramm wird nicht richtig ausgeführt


Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute, muss zum Männertag nochmal ein paar Fragen stellen.
Also folgendes:
Ich habe eine Interruptroutine die die mir den Rückgabewert einer darin 
enthaltenen Funktion auf eine globale Variable zurückgibt(ich lasse mir 
den Status ausgeben).
Die darin enthaltene Funktion holt empfangene Daten vom USART ab.
Diese Variable lasse ich im Hauptprogramm überwachen und wenn sie 1 wird 
dann wird ein Bestätigungsstring gesendet.
Dies funktioniert grundsätzlich aber wenn ich etwas an den Controller 
sende, wird mir zwar der Status richtig ausgegeben (1) aber der 
Antwortstring wird nicht gesendet. Erst wenn ich 2 weitere male einen 
String sendet wird der Antwortstring gesendet, obwohl die Variable, an 
die die Bedingung geknüpft ist, schon beim ersten mal 1 war.
Hier noch der Code damit ihr es besser versteht:;

#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h> // Für "strcmp"
#include "kesselfunktionen.h"



#define Takt 7372800
#define BAUD1 9600        // Baudrate

#define UBRR_VAL1 ((Takt/16/BAUD1)-1)   // Schnittstelle BUS

#define BAUD2 9600          // Baudrate
#define UBRR_VAL2 ((Takt/16/BAUD2)-1)   // Schnittstelle KESSEL

volatile unsigned char nutzdaten[60],steuerdaten[2];
volatile int receiveflag = 0;

ISR(USART0_RXC_vect)
{


receiveflag = USART0_readcommand(nutzdaten, steuerdaten, UDR0);
USART_KESSEL_Transmit(receiveflag); //hier wird der Status ausgegeben, es wird gleich nach dem ersten empfangen eine 1 ausgegeben
};



int main(void)
{
UCSR0A |= (1<<RXC0);
UCSR0B |= (1<<TXEN0);                // USART1 TX einschalten
UCSR1B |= (1<<TXEN1);                // USART2 TX einschalten
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0);
UCSR1B |= (1<<RXEN1);
UCSR0C |= (1<<URSEL0)|(3<<UCSZ00);    // USART1 Asynchron 8N1
UCSR1C |= (1<<URSEL1)|(3<<UCSZ10);    // USART2 Asynchron 8N1

UBRR0H = UBRR_VAL1 >> 8;
UBRR1H = UBRR_VAL2 >> 8;
UBRR0L = UBRR_VAL1;
UBRR1L = UBRR_VAL2 ;

while (!bit_is_set(UCSR0A, UDRE0));

//Timer initialisieren
//    TCCR0 |= (1<<CS00)|(1<<CS02);
//    TIMSK =(1<<TOIE0);
//    sei();//interrupts aktivieren






    DDRA = (0 << DDA7); // Pin  A7 als Eingang definieren
    //USART_KESSEL_Transmit(USART1_readcommand(nutzdaten, steuerdaten));
    initialisierung();
    sei();


do
{
  if(receiveflag== 1) //hier wird der Quittierungsstring gesendet aber erst nach dem dritten mal
  {
    quittierung_kessel(steuerdaten);
  }
  receiveflag = 0;
}while(1);
while(1);
}

Autor: magnetus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es ist eine sehr weise Entscheidung, uns die "kesselfunktionen.h" 
vorzuenthalten.

Gruß,
Magnetus

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
do
{
  if(receiveflag== 1)
  {
    quittierung_kessel(steuerdaten);
  }
  receiveflag = 0;
}
Davon, dass receiveflag unnötigerweise eine 16-Bit Variable ist, mal 
abgesehen, was ist, wenn der Interrupt zwischen "if(receiveflag== 1)" 
und "receiveflag = 0;" auftritt? Dann geht dir das gesetzte Flag 
unbemerkt flöten. Und da dieser verbotene Bereich obendrein auch noch 
größer sein wird, als der erlaubte, ist es kein Wunder, dass der 
Interrupt mehrmals auftreten muss, bis er zufällig mal in den erlaubten 
Bereich fällt und das Flag bemerkt wird.

Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also hab receiveflag auf unsigned char geändert. aber funktioniert 
trotzdem nicht zufriedenstellend.
Wie macht man denn sowas am besten?
also es kommt ein String von einer Steuerung, dieser wird in meinen 
Interrupt in ein Array gespeichert zur späteren Verwendung.
Wenn die Routine fertig ist. Dann wird an Receiveflag eine 1 übergeben.
Dann wird in meiner If Abfrage ein Bestätigungsstring gesendet. Das soll 
immer so hin und her gehen. Später soll vor dem Senden des 
Bestätigungsstring noch was anderes ausgeführt werden. Die andere Anlage 
sendet nur nachdem sie den kompletten Bestäigungsstring erhalten hat, 
wird mir also beim senden nicht dazwischen funken.
Hier mal noch der Code vom Bestätigungsstring:
char temp_kommando_kessel(void)
{
  int i;
  unsigned char kommando[8];
  kommando[0] = 0x52;
  kommando[1] = 0x62;
  kommando[2] = 0x03;
  kommando[3] = 0x00;
  kommando[4] = 0x00;
  kommando[5] = 0x01;
  kommando[6] = 0x00;
  kommando[7] = 0xB8;
  for( i = 0; i < 9; i++)
  {
    USART_KESSEL_Transmit(kommando[i]);
  }
  return 1;
}

Wie macht man sowas denn richtig ? Soll ja später auch ordentlich 
funktionieren und ich hab leider keinen besseren Ansatz...
Wär schön wenn ihr sagt wie ihr sowas richtig macht.

Noch eine FRage, wäre es auch möglich den Status in einem Bitfeld zu 
speichern? Ich frage deswegen weil ich es dann ja als volatile 
deklarieren muss und ob das geht.

Autor: Chronist (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Pack
receiveflag = 0;
in den if-Teil

Was du geschrieben hast erlaubt die Löschung des Flags ohne der String 
gesendet wurde. Interrupts können das Hauptprogramm schließlich an jeder 
Stelle unterbrechen!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Hohmann schrieb:

> Wär schön wenn ihr sagt wie ihr sowas richtig macht.

Richtig macht man es, indem man sich vergegenwärtigt, dass ein Interrupt 
an jeder beliebigen Stelle kommen kann.
zb auch
do
{
  if(receiveflag== 1)
  {
    quittierung_kessel(steuerdaten);
  }

  <------- HIER ------>

  receiveflag = 0;
}

an der markeirten Position. Da kommt jetzt der Interrupt. In der ISR 
wird festgestellt, das alles fertig ist, receiveflag wird auf 1 gestellt 
und die ISR verlassen.
Nur leider, leider. Die nächste Anweisung in der Hauptschleife stellt 
receiveflag wieder zurück auf 0.
Und damit hat sich die 'Fertig'-Meldung aus der ISR in Schall und Rauch 
aufgelöst.

Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Leute ich hab ein zwietes großen Problem.
und zwar sendet die Steuerung leider nach manchen Quittierung zwei 
Befehle hintereinander ohne eine Quittierung dazwischen.
Ich benötige leider den zweiten string zum auswerten. Leider schickt 
mein µc schon nach dem ersten Telegramm eine Quittierung raus, aber ist 
die falsche. Habe versucht das nur ausführen zu lassen wenn das Bit RXC0 
im USART Register UCSR0A nicht gesetzt ist aber irgendwie wird das nicht 
schnell genug gesetzt ich weis auch nicht.
Hier erstmal der vollständige Code:
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h> // Für "strcmp"
#include "kesselfunktionen.h"



#define Takt 7372800
#define BAUD1 9600        // Baudrate

#define UBRR_VAL1 ((Takt/16/BAUD1)-1)   // Schnittstelle BUS

#define BAUD2 9600          // Baudrate
#define UBRR_VAL2 ((Takt/16/BAUD2)-1)   // Schnittstelle KESSEL

volatile unsigned char nutzdaten[60],steuerdaten[2];
volatile unsigned char receiveflag = 0,temprequest = 0;

ISR(USART0_RXC_vect)
{


receiveflag = USART0_readcommand(nutzdaten, steuerdaten, UDR0);
USART_KESSEL_Transmit(receiveflag);

};



int main(void)
{
UCSR0A |= (1<<RXC0);
UCSR0B |= (1<<TXEN0);                // USART1 TX einschalten
UCSR1B |= (1<<TXEN1);                // USART2 TX einschalten
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0);
UCSR1B |= (1<<RXEN1);
UCSR0C |= (1<<URSEL0)|(3<<UCSZ00);    // USART1 Asynchron 8N1
UCSR1C |= (1<<URSEL1)|(3<<UCSZ10);    // USART2 Asynchron 8N1

UBRR0H = UBRR_VAL1 >> 8;
UBRR1H = UBRR_VAL2 >> 8;
UBRR0L = UBRR_VAL1;
UBRR1L = UBRR_VAL2 ;

while (!bit_is_set(UCSR0A, UDRE0));

//Timer initialisieren
//    TCCR0 |= (1<<CS00)|(1<<CS02);
//    TIMSK =(1<<TOIE0);
//    sei();//interrupts aktivieren






    DDRA = (0 << DDA7); // Pin  A7 als Eingang definieren
    //USART_KESSEL_Transmit(USART1_readcommand(nutzdaten, steuerdaten));
    initialisierung();
    sei();


do
{

  if(receiveflag== 1)
  {
      if(bit_is_set(UCSR0A, UDRE0))
      {
    receiveflag = quittierung_kessel(steuerdaten);
    int j;
      }
  }

/*  if(steuerdaten[1] == 0x32 && temprequest == 0)
  {
    temprequest = temp_kommando_kessel();
  };
*/

}while(1);
while(1);
}

hier noch die Routinen:

int USART0_readcommand(unsigned char *nutzdaten, unsigned char *steuerdaten, char start)
{

  unsigned char  NextByte;
   char  Len;
  unsigned int   i;
  unsigned int   CheckSummCalc;
  unsigned int   CheckSummRead;

  CheckSummCalc = 0;

  steuerdaten[0] = start;   // CommandoByte 1
  CheckSummCalc += steuerdaten[0];
  steuerdaten[1] = USART_KESSEL_getchar();   // CommandoByte 2
  CheckSummCalc += steuerdaten[1];


 // Anzahl der Datenbytes lesen
  Len = USART_KESSEL_getchar();
  CheckSummCalc += Len;
  // Jetzt kommen die Datenbytes


  for( i = 0; i < Len; i++ )
  {
    NextByte = USART_KESSEL_getchar();
    CheckSummCalc += NextByte;
    nutzdaten[i] = NextByte;
  }

  // Daten sind gelesen. Jetzt kommen noch 2 Bytes Checksumme
  CheckSummRead = USART_KESSEL_getchar();
  CheckSummRead = ( CheckSummRead << 8 ) + USART_KESSEL_getchar();

  if(CheckSummRead == CheckSummCalc)
  {
    return 1;
  }else
  {
    return 0;
  }
}


char temp_kommando_kessel(void)
{
  int i;
  unsigned char kommando[8];
  kommando[0] = 0x52;
  kommando[1] = 0x62;
  kommando[2] = 0x03;
  kommando[3] = 0x00;
  kommando[4] = 0x00;
  kommando[5] = 0x01;
  kommando[6] = 0x00;
  kommando[7] = 0xB8;
  for( i = 0; i < 9; i++)
  {
    USART_KESSEL_Transmit(kommando[i]);
  }
  return 1;
}


Wenn ich jetzt von der Steuerung 4D410C49001C072B5A757374616E64041A 
kommt
(4D und 41 sind steuerdaten, 041A ist die Checksumme) dann läuft meine 
Empfangsfunktion sauber durch. und die Quittierung wird gesendet.

Wenn jetzt aber deieser string kommt: 5261010100B54D4105530001000200E9
dann hat die funktion ein Problem weil bei 5261010100B5 (checksumme 
00B5) ist der string für die Funktion zuende und die beendet sich dann.
Das dahinter aber noch 4D4105530001000200E9 kommt wird ignoriert.
Das hab ich versucht mit if(bit_is_set(UCSR0A, RXC0)) abzu fangen aber 
irgendwie funktioniert es nicht so wie ich mir das gedacht hatte.
Denn gleich nach dem ersten string wird die Quittierung gesendet, das 
soll aber erst nach dem 2. geschehen. Der erst soll ignoriert werden.

Wär schön wenn mir nochmal jemand einen Tip geben könnte.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Hohmann schrieb:

> Wenn jetzt aber deieser string kommt: 5261010100B54D4105530001000200E9
> dann hat die funktion ein Problem weil bei 5261010100B5 (checksumme
> 00B5) ist der string für die Funktion zuende

welche Funktion?

> und die beendet sich dann.

Hä?

> Das dahinter aber noch 4D4105530001000200E9 kommt wird ignoriert.

Die kommt dann eben bei der nächsten Runde drann

> Das hab ich versucht mit if(bit_is_set(UCSR0A, RXC0))

Das ist doch Quatsch.

> Denn gleich nach dem ersten string wird die Quittierung gesendet,

Na dan sende die einfach noch nicht sondern erst dann, wenn du den 
String tatsächlich ausgewertet hast und weißt dass du eine Quittierung 
senden willst.

> das
> soll aber erst nach dem 2. geschehen. Der erst soll ignoriert werden.

Woher weißt du, das der ignoriert werden muss?

> Wär schön wenn mir nochmal jemand einen Tip geben könnte.

Du beschwerst dich dass du die Quittierung zu früh sendest, bist aber 
der Programmierer, der genau das programmiert hat.

Irgendwie hab ich ein Problem, dein Problem zu verstehen

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die hier
char temp_kommando_kessel(void)
{
  int i;
  unsigned char kommando[8];
  kommando[0] = 0x52;
  kommando[1] = 0x62;
  kommando[2] = 0x03;
  kommando[3] = 0x00;
  kommando[4] = 0x00;
  kommando[5] = 0x01;
  kommando[6] = 0x00;
  kommando[7] = 0xB8;
  for( i = 0; i < 9; i++)
  {
    USART_KESSEL_Transmit(kommando[i]);
  }
  return 1;
}

solltest du dir nochmal ansehen. Warum schickst du 9 Bytes, wenn du nur 
8 hast?

Gewöhn dir für solche Sachen (for-Schleifen, die auf einem Array 
arbeiten) an, das dir der Compiler Arbeit abnimmt. Weder musst du selber 
die ANzahl der Bytes abzählen, noch musst du diese Anzahl selber in der 
for-Schleife einsetzen. Kann alles der Compiler, und zwar fehlerfrei(!), 
erledigen.
char temp_kommando_kessel(void)
{
  int i;
  unsigned char kommando[] = { 0x52, 0x62, 0x03, 0x00, 0x00, 0x01, 0x00, 0xB8 };

  for( i = 0; i < sizeof( kommando ) / sizeof( *kommando ); i++ )
  {
    USART_KESSEL_Transmit(kommando[i]);
  }
  return 1;
}

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, aber deine ganze Grundstruktur ist schon etwas verquer.
ISR(USART0_RXC_vect)
{
receiveflag = USART0_readcommand(nutzdaten, steuerdaten, UDR0);
...

int USART0_readcommand(unsigned char *nutzdaten, unsigned char *steuerdaten, char start)
{
...
  for( i = 0; i < Len; i++ )
  {
    NextByte = USART_KESSEL_getchar();
Der Interrupt zeigt an, dass ein Zeichen zur Abholung bereit steht, und 
in der ISR solltest du auch nur genau ein Zeichen verarbeiten. In der 
ISR auf das Eintreffen weiterer Zeichen zu warten, ist praktisch nie 
sinnvoll. Wenn es tatsächlich bei einer simplen Ping-Pong-Kommunikation 
bleiben soll, mag das vielleicht noch gehen (wobei man dann aber auch 
gleich ganz auf die Verwendung des Interrupts verzichten kann), aber 
spätestens dann, wenn noch andere Sachen dazukommen, wirst du von der 
Tatsache in den Bauch gekniffen, dass der Controller Ewigkeiten(!) mit 
nutzlosem Warten in der ISR verbringt.

Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bei einem normalen Telegramm wird die ISR beendet wenn auch die Routine 
USART_readcommand fertig ist, da ja immer ein ganzer Datensatz von der 
anderen Steuerung kommt.
Wie soll ich das nun am besten grundsätzlich machen mit der 
Kommunikation.
es kommt von der Steuerung:
4D410C49001C072B5A757374616E64041A

mehr nicht bis ich antworte:
4d 41 01 01 00 90

das geht eiegentlich immer so hin und her.
Es kommt jedoch wenn ich etwas einstellen will(ein anderer 
Quittungsstring) ein solcher Quittierungsstring von der Steuerung:
5261010100B5 4D4105530001000200E9

dann muss ich aber den zweiten String Quittieren und nicht den ersten 
(das weis ich weil ich einen kommplette Kommunikationslog von dieser 
Steuerung mit einem anderen PC habe).
Ich habe mir warscheinlich mit der ISR ein wenig verrannt. Wie macht man 
es richtig?
Ja es soll zwischen dem senden des Quittungsstrings auch noch was 
anderes gemacht werden.

Autor: ozo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohne mich jetzt wirklich in dein Problem reingedacht zu haben, würde ich 
einen anderen Ansatz wählen. Die Kommunikation läuft grundsätzlich über 
(Ring)Puffer. Deine Applikation will was senden? Ab in den Puffer und 
Sendeinterrupt die Arbeit machen lassen. Eventuell wird der Applikation 
der Sendestatus (alles gut, fehler...) asynchron mitgeteilt.
Die empfangende Interruptroutine schreibt in einen anderen Puffer und 
signalisiert der Apllikation einfach nur den Empfang eines Zeichens. Die 
Applikation muss dann in den Puffer schauen und sich überlegen, ob der 
Inhalt irgend eine sinnvolle Bedeutung hat. Wie sie dann reagiert, ist 
dein Bier...
Hast du eventuell eine Dokumentation der vorkommenden Mitteilungen? Oder 
nur den Mittschnitt? Mit Glück haben die einzelnen Mitteilungen doch 
eine sinnvolle Struktur. Vielleicht solltest du dich dann von dieser 
Seite dem Problem nähern: "Wenn diese und jene Mitteilung kommt, muß ich 
das und das machen" - die Struktur des Programms wird sich daraus 
ergeben.
Jedenfalls wäre aus meiner Sicht gescheite Sende- und Empfangsroutinen 
ein sinnvoller Start. Beispiele solltes du hier im Forum zuhauf finden.

Grüsse

Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Erklärung aber ich denke ich werd es wohl ohne einen 
Interrupt machen.Finde einfach keine Grundstruktur wie ich das Programm 
aufbauen soll weil ich ja wenn ein Interrupt kommt der nur ein Zeichen 
in ein Array schriebt noch nichts mit dem Zeichen anfangen kann, bzw. 
meine Auswerteroutine erst anfängt wenn ein kompletter Datensatz 
eingelesen ist.
Aber das weis ich ja erst wenn ich das dritte byte empfangen hab(das 
gibt die anzahl der noch kommenden Zeichen an) und die Checksumme mit 
der gesendeten verglichen hab.
Der Interrupt speichert ständig in ein neuen Arrayplatz, diesen muss ich 
aber nach einen kompletten String wieder auf 0 setzen damit der nicht 
endlos das array vollschreibt.
Aber ich kann meine Empfangsroutine erst durchlaufen lassen wenn ein 
kompletter Datensatz drin ist sonst ließt das ja Müll von den noch nicht 
beschriebenen Arrayplätzen.
Ich weis nicht wie ich die Routine verändern soll...

Autor: ozo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde an deiner Stelle die einzelnen Module soweit wie möglich 
trennen.
Was juckts den Interrupt, was er da empfängt. Zeichen wird empfangen, in 
den Puffer geschrieben und dieses wird dem Hauptprogramm signalisiert.
Dieses kann dann gucken, ob das dritte Byte überhaupt schon empfangen 
wurde, ob die Checksum stimmt und was weiß ich.
Klar, dadurch das man bei jedem empfangenen Zeichen den gesammten Puffer 
analysiert, verschwendet man ein bisschen Rechenzeit.
Aber den Nachteil würde ich in Kauf nehmen, wenn ich im Gegenzug eine 
saubere Trennung zwischen Interrupts, Datentransport und 
Applikationlogik kriegen würde.
Such doch mal hier im Forum, das sollte es Beispiele für 
interruptgetriebene USART Kommunikation bis zum Erbrechen geben.

Grüsse

Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann hab ich mal ne Frage zu arrays.
Kann ein array eine dynamischer Größe bekommen?
Weil wie soll ich feststellen das das drite byte schon gekommen ist und 
nicht irgendein Müll im array steht? Durch ne extra variable die im 
Interrupt hochgezählt wird?

Wenn ich mit cli() Interrupt abschslte, dann gehen mir doch Interrupt 
verloren oder werden die gespeichert und später noch ausgeführt wenn ich 
sie wieder zulasse?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Hohmann schrieb:
> Durch ne extra variable die im Interrupt hochgezählt wird?

Ja. Die brauchst du ja sowieso, schließlich muss ja auch die ISR wissen, 
wo im Array das nächste Zeichen hinzuschreiben ist.

> oder werden die gespeichert und später noch ausgeführt wenn ich
> sie wieder zulasse?

Ja.

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>> oder werden die gespeichert und später noch ausgeführt wenn ich
>> sie wieder zulasse?

>Ja.

Genaugenommen wird eine Interruptaufforderung pro Interruptquelle 
gespeichert. Wenn deine Wartephase zu lang ist, gehen Interrups 
verloren.

MfG Spess

Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich habs jetzt nochmal probiert.
Leider sind die Ergebnisse ernüchternd.
Hier mal mein Code:
int Telegrammauswertung(unsigned char *nutzdaten)
{
  extern volatile int length;
  extern volatile int arrayplace;
  extern volatile char lengthflag;

  if(arrayplace == 2)
  {
    length = nutzdaten[2];
    lengthflag = 1;
    USART_KESSEL_Transmit(length);
  }
  if(arrayplace == (length+5) && lengthflag == 1)
  {
    int i;
    char checksummcalc= 0, checksummread;
    for(i=0;i<length+3;i++)
    {
      checksummcalc += nutzdaten[i];
    }
    checksummread = (nutzdaten[length+4] << 8) + nutzdaten[length+5];
    arrayplace = 0;
    lengthflag = 0;
    if (checksummread == checksummcalc)
    {

      return 1;
    }else return 0;

  }
}

in der Main

#define Takt 7372800
#define BAUD1 9600        // Baudrate

#define UBRR_VAL1 ((Takt/16/BAUD1)-1)   // Schnittstelle BUS

#define BAUD2 9600          // Baudrate
#define UBRR_VAL2 ((Takt/16/BAUD2)-1)   // Schnittstelle KESSEL

volatile unsigned char nutzdaten[60],steuerdaten[2];
volatile unsigned char receiveflag = 0, lengthflag = 0, temprequest = 0;
volatile int arrayplace = 0, length = 0;

ISR(USART0_RXC_vect)
{


nutzdaten[arrayplace]=UDR0;
//USART_KESSEL_Transmit(arrayplace);
arrayplace++;

};



int main(void)
{
UCSR0A |= (1<<RXC0);
UCSR0B |= (1<<TXEN0);                // USART1 TX einschalten
UCSR1B |= (1<<TXEN1);                // USART2 TX einschalten
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0);
UCSR1B |= (1<<RXEN1);
UCSR0C |= (1<<URSEL0)|(3<<UCSZ00);    // USART1 Asynchron 8N1
UCSR1C |= (1<<URSEL1)|(3<<UCSZ10);    // USART2 Asynchron 8N1

UBRR0H = UBRR_VAL1 >> 8;
UBRR1H = UBRR_VAL2 >> 8;
UBRR0L = UBRR_VAL1;
UBRR1L = UBRR_VAL2 ;

while (!bit_is_set(UCSR0A, UDRE0));

    DDRA = (0 << DDA7); // Pin  A7 als Eingang definieren
    initialisierung();
    sei();


do
{
  receiveflag = Telegrammauswertung(nutzdaten);

}while(1);
while(1);
}



Wenn ich jetzt folgenden String sende:


52610300000100B7

sollte ich von der Routine Telegrammauswertung eigentlich  nur einmal 
ein Zeichen zurück bekommen wegen:
if(arrayplace == 2)
  {
    length = nutzdaten[2];
    lengthflag = 1;
    USART_KESSEL_Transmit(length);
  }
da sollte dann eignetlich 03 drin stehen. aber
ich bekomme gleich 6 Zeichen zurück: 00 00 00 03 03 03 obwohl es nur das 
eine sein sollte, da arrayplace eigentlich nur einmal 2 sein kann.

Wo liegt da schonwieder der Denkfehler?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Christian Hohmann (chris0086)

>Hier mal mein Code:

>int Telegrammauswertung(unsigned char *nutzdaten)
>{
>  extern volatile int length;
>  extern volatile int arrayplace;
>  extern volatile char lengthflag;

Lokale Variablen in einer Funktion können nie extern sein, und volatile 
ist dort auch fast immer sinnlos.

>Wo liegt da schonwieder der Denkfehler?

U.a., dass du mal wieder die Netiquette ignoriert hast. Lange 
Quelltexte gehören in den Anhang!

Und solche Fragmente sind nur sehr bedingt sinnvoll.

MFG
Falk

Autor: Chris Tian (chris0086)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tut mir leid das mit dem Quelltext, kommt nicht wieder vor.
Ich habe zu dem extern gelesen:
Mit

extern char a;
wird eine Variable deklariert. Das Schlüsselwort extern in obigem 
Beispiel besagt, dass die Definition der Variablen a irgendwo in einem 
anderen Modul des Programms liegt. So deklariert man Variablen, die 
später beim Binden (Linken) aufgelöst werden. Da in diesem Fall kein 
Speicherplatz reserviert wurde, handelt es sich um keine Definition. Der 
Speicherplatz wird erst über

 char a;
reserviert, was in irgend einem anderen Quelltextmodul erfolgen muß.


Damit wollte ich meine "globalen" Variablen auch in der Funktion 
Telegrammauswertung nutzbar machen, weil diese in einer anderen C Datei 
gespeichert wird.
Ich wollte die Variablen nicht beim Funktionsaufruf übergeben,, weil ich 
dachte das wenn innerhalb der Funktionsabarbeitung ein Interrupt 
ausgelöst wird, die Funktion dann mit einem alten WErt von arrayplace 
arbeitet oder ist das unbegründet?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für meinen Geschmack vermischt du immer noch Empfangen/Zwischenspeichern 
und Auswerten der Daten viel zu sehr.

Ich würde mir an deiner Stelle erst mal die UART-Library vom P.Fleury 
holen. Damit ist der Teil 'Empfangsbuffer' schon mal komplett erledigt.

Und für den Rest würde ich mir eine State-Maschine aufsetzen
#define COMMAND_BYTE_1   1
#define COMMAND_BYTE_2   2
#define LENGTH_BYTE      3
#define DATA_BYTES       4
#define CHECKSUM_BYTE_1  5
#define CHECKSUM_BYTE_2  6

...

  uint16_t command;
  uint8_t  length;
  uint8_t  dataLength;
  uint8_t  data[30];    // genug für das längste Kommando
  uint16_t checksum;
  unsigned int c;
  uint8_t  byte;

  state = COMMAND_BYTE_1;    // das nächste Byte das reinkommt, ist das
                             // erste Byte des Kommandos

  while( 1 ) {

    c = uart_getc();
    if( ( c & 0xFF00 ) == 0 ) {    // wurde etwas empfangen?
      byte = c;                    // High Byte strippen, nur das Datenbyte bleibt

                                   // je nachdem welche Teile das Protokolls bis jetzt
                                   // abgearbeitet wurden, das Byte in die richtigen
                                   // Kanäle bringen  
      switch( state ) {
        case COMMAND_BYTE_1:          // das erste Byte des Kommandos ist da
          command = byte << 8;        // speichern
          state = COMMAND_BYTE_2;     // das nächste Byte wird das 2. Byte vom Kommando sein
          break;

        case COMMAND_BYTE_2:          // das 2.te Byte vom Kommando ist angekommen
          command |= byte;
          state = LENGTH_BYTE;
          break;

        case LENGTH_BYTE:             // Auch das LENGTH Byte ist schon da
          length = 0;
          dataLength = byte;
          state = DATA_BYTES;
          break;

        case DATA_BYTES:             // Soviele Datenbytes wie im Length Byte angekündigt empfangen
          data[ length++ ] = byte;
          if( length == dataLength )
            state = CHECKSUM_BYTE_1;
          break;

        case CHECKSUM_BYTE_1:        // erster Teil der Checksumme
          checksum = byte << 8;
          state = CHECKSUM_BYTE_2;
          break;

        case CHECKSUM_BYTE_2:        // zweites Byte der Checksumme
          checksum |= byte;

          // hier ist jetzt das Datenpaket vollständig
          // und wird ausgewertet

          ....

          // nach der Auswertung gehts wieder weiter, indem auf das erste
          // Kommandobyte gewartet wird
          state = COMMAND_BYTE_1;
          break;
      }
    }
  }


Die Statemaschine würde ich in eine eigene Funktion auslagern, die 0 
oder 1 zurückliefert, je nachdem ob ein Datenpaket mit dem übergebenen 
nächsten empfangenen Byte vollständig wurde oder nicht.
uint8_t appendByte( uint8_t byte )
{
  uint8_t isComplete = 0;
  static uint8_t state = COMMAND_BYTE_1;

  switch( state ) {
    case COMMAND_BYTE_1:          // das erste Byte des Kommandos ist da
      command = byte << 8;        // speichern
      state = COMMAND_BYTE_2;     // das nächste Byte wird das 2. Byte vom Kommando sein
      break;

    case COMMAND_BYTE_2:          // das 2.te Byte vom Kommando ist angekommen
      command |= byte;
      state = LENGTH_BYTE;
      break;

    case LENGTH_BYTE:             // Auch das LENGTH Byte ist schon da
      length = 0;
      dataLength = byte;
      state = DATA_BYTES;
      break;

    case DATA_BYTES:             // Soviele Datenbytes wie im Length Byte angekündigt empfangen
      data[ length++ ] = byte;
      if( length == dataLength )
        state = CHECKSUM_BYTE_1;
      break;

    case CHECKSUM_BYTE_1:        // erster Teil der Checksumme
      checksum = byte << 8;
      state = CHECKSUM_BYTE_2;
      break;

    case CHECKSUM_BYTE_2:        // zweites Byte der Checksumme
      checksum |= byte;
      isComplete = 1;
      state = COMMAND_BYTE_1;
      break;
  }

  return isComplete ;
}

...
  while( 1 ) {

    c = uart_getc();
    if( ( c & 0xFF00 ) == 0 ) {    // wurde etwas empfangen?
      if( appendByte( c ) ) {
        .....
        hier die Auswertung des Datenpakets
        .....
      }
    }
  }

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Hohmann schrieb:
> Damit wollte ich meine "globalen" Variablen auch in der Funktion
> Telegrammauswertung nutzbar machen, weil diese in einer anderen C Datei
> gespeichert wird.

Dazu müssen das dann aber auch in dieser C-Datei globale Variablen sein, 
und keine lokalen, müssen also außerhalb der Funktion stehen.

Variablen und ihre Typen scheinen eh noch ein größerer Schwachpunkt bei 
dir zu sein:
    int i;
    char checksummcalc= 0, checksummread;
    for(i=0;i<length+3;i++)
    {
      checksummcalc += nutzdaten[i];
    }
    checksummread = (nutzdaten[length+4] << 8) + nutzdaten[length+5];
Hier ist i unnötig groß. Dafür sind checksummcalc und checksummread zu 
klein, denn die letzte Zeile offenbart mir, dass die Checksumme 
offensichtlich 16 Bit groß ist. Von dem "zu klein" abgesehen, schreibe 
dir bitte folgendes hinter die Ohren:
Wenn du mit den Variablen rechnest, dann benutze niemals nur "char", 
sondern immer explizit "unsigned char" oder "signed char", oder noch 
besser gleich uint8_t oder int8_t.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.