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


von Chris T. (chris0086)


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:;
1
#include <stdint.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <stdlib.h>
5
#include <string.h> // Für "strcmp"
6
#include "kesselfunktionen.h"
7
8
9
10
#define Takt 7372800
11
#define BAUD1 9600        // Baudrate
12
13
#define UBRR_VAL1 ((Takt/16/BAUD1)-1)   // Schnittstelle BUS
14
15
#define BAUD2 9600          // Baudrate
16
#define UBRR_VAL2 ((Takt/16/BAUD2)-1)   // Schnittstelle KESSEL
17
18
volatile unsigned char nutzdaten[60],steuerdaten[2];
19
volatile int receiveflag = 0;
20
21
ISR(USART0_RXC_vect)
22
{
23
24
25
receiveflag = USART0_readcommand(nutzdaten, steuerdaten, UDR0);
26
USART_KESSEL_Transmit(receiveflag); //hier wird der Status ausgegeben, es wird gleich nach dem ersten empfangen eine 1 ausgegeben
27
};
28
29
30
31
int main(void)
32
{
33
UCSR0A |= (1<<RXC0);
34
UCSR0B |= (1<<TXEN0);                // USART1 TX einschalten
35
UCSR1B |= (1<<TXEN1);                // USART2 TX einschalten
36
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0);
37
UCSR1B |= (1<<RXEN1);
38
UCSR0C |= (1<<URSEL0)|(3<<UCSZ00);    // USART1 Asynchron 8N1
39
UCSR1C |= (1<<URSEL1)|(3<<UCSZ10);    // USART2 Asynchron 8N1
40
41
UBRR0H = UBRR_VAL1 >> 8;
42
UBRR1H = UBRR_VAL2 >> 8;
43
UBRR0L = UBRR_VAL1;
44
UBRR1L = UBRR_VAL2 ;
45
46
while (!bit_is_set(UCSR0A, UDRE0));
47
48
//Timer initialisieren
49
//    TCCR0 |= (1<<CS00)|(1<<CS02);
50
//    TIMSK =(1<<TOIE0);
51
//    sei();//interrupts aktivieren
52
53
54
55
56
57
58
    DDRA = (0 << DDA7); // Pin  A7 als Eingang definieren
59
    //USART_KESSEL_Transmit(USART1_readcommand(nutzdaten, steuerdaten));
60
    initialisierung();
61
    sei();
62
63
64
do
65
{
66
  if(receiveflag== 1) //hier wird der Quittierungsstring gesendet aber erst nach dem dritten mal
67
  {
68
    quittierung_kessel(steuerdaten);
69
  }
70
  receiveflag = 0;
71
}while(1);
72
while(1);
73
}

von magnetus (Gast)


Lesenswert?

Es ist eine sehr weise Entscheidung, uns die "kesselfunktionen.h" 
vorzuenthalten.

Gruß,
Magnetus

von Stefan E. (sternst)


Lesenswert?

1
do
2
{
3
  if(receiveflag== 1)
4
  {
5
    quittierung_kessel(steuerdaten);
6
  }
7
  receiveflag = 0;
8
}
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.

von Chris T. (chris0086)


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:
1
char temp_kommando_kessel(void)
2
{
3
  int i;
4
  unsigned char kommando[8];
5
  kommando[0] = 0x52;
6
  kommando[1] = 0x62;
7
  kommando[2] = 0x03;
8
  kommando[3] = 0x00;
9
  kommando[4] = 0x00;
10
  kommando[5] = 0x01;
11
  kommando[6] = 0x00;
12
  kommando[7] = 0xB8;
13
  for( i = 0; i < 9; i++)
14
  {
15
    USART_KESSEL_Transmit(kommando[i]);
16
  }
17
  return 1;
18
}

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.

von Chronist (Gast)


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!

von Karl H. (kbuchegg)


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
1
do
2
{
3
  if(receiveflag== 1)
4
  {
5
    quittierung_kessel(steuerdaten);
6
  }
7
8
  <------- HIER ------>
9
10
  receiveflag = 0;
11
}

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.

von Chris T. (chris0086)


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:
1
#include <stdint.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <stdlib.h>
5
#include <string.h> // Für "strcmp"
6
#include "kesselfunktionen.h"
7
8
9
10
#define Takt 7372800
11
#define BAUD1 9600        // Baudrate
12
13
#define UBRR_VAL1 ((Takt/16/BAUD1)-1)   // Schnittstelle BUS
14
15
#define BAUD2 9600          // Baudrate
16
#define UBRR_VAL2 ((Takt/16/BAUD2)-1)   // Schnittstelle KESSEL
17
18
volatile unsigned char nutzdaten[60],steuerdaten[2];
19
volatile unsigned char receiveflag = 0,temprequest = 0;
20
21
ISR(USART0_RXC_vect)
22
{
23
24
25
receiveflag = USART0_readcommand(nutzdaten, steuerdaten, UDR0);
26
USART_KESSEL_Transmit(receiveflag);
27
28
};
29
30
31
32
int main(void)
33
{
34
UCSR0A |= (1<<RXC0);
35
UCSR0B |= (1<<TXEN0);                // USART1 TX einschalten
36
UCSR1B |= (1<<TXEN1);                // USART2 TX einschalten
37
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0);
38
UCSR1B |= (1<<RXEN1);
39
UCSR0C |= (1<<URSEL0)|(3<<UCSZ00);    // USART1 Asynchron 8N1
40
UCSR1C |= (1<<URSEL1)|(3<<UCSZ10);    // USART2 Asynchron 8N1
41
42
UBRR0H = UBRR_VAL1 >> 8;
43
UBRR1H = UBRR_VAL2 >> 8;
44
UBRR0L = UBRR_VAL1;
45
UBRR1L = UBRR_VAL2 ;
46
47
while (!bit_is_set(UCSR0A, UDRE0));
48
49
//Timer initialisieren
50
//    TCCR0 |= (1<<CS00)|(1<<CS02);
51
//    TIMSK =(1<<TOIE0);
52
//    sei();//interrupts aktivieren
53
54
55
56
57
58
59
    DDRA = (0 << DDA7); // Pin  A7 als Eingang definieren
60
    //USART_KESSEL_Transmit(USART1_readcommand(nutzdaten, steuerdaten));
61
    initialisierung();
62
    sei();
63
64
65
do
66
{
67
68
  if(receiveflag== 1)
69
  {
70
      if(bit_is_set(UCSR0A, UDRE0))
71
      {
72
    receiveflag = quittierung_kessel(steuerdaten);
73
    int j;
74
      }
75
  }
76
77
/*  if(steuerdaten[1] == 0x32 && temprequest == 0)
78
  {
79
    temprequest = temp_kommando_kessel();
80
  };
81
*/
82
83
}while(1);
84
while(1);
85
}

hier noch die Routinen:
1
int USART0_readcommand(unsigned char *nutzdaten, unsigned char *steuerdaten, char start)
2
{
3
4
  unsigned char  NextByte;
5
   char  Len;
6
  unsigned int   i;
7
  unsigned int   CheckSummCalc;
8
  unsigned int   CheckSummRead;
9
10
  CheckSummCalc = 0;
11
12
  steuerdaten[0] = start;   // CommandoByte 1
13
  CheckSummCalc += steuerdaten[0];
14
  steuerdaten[1] = USART_KESSEL_getchar();   // CommandoByte 2
15
  CheckSummCalc += steuerdaten[1];
16
17
18
 // Anzahl der Datenbytes lesen
19
  Len = USART_KESSEL_getchar();
20
  CheckSummCalc += Len;
21
  // Jetzt kommen die Datenbytes
22
23
24
  for( i = 0; i < Len; i++ )
25
  {
26
    NextByte = USART_KESSEL_getchar();
27
    CheckSummCalc += NextByte;
28
    nutzdaten[i] = NextByte;
29
  }
30
31
  // Daten sind gelesen. Jetzt kommen noch 2 Bytes Checksumme
32
  CheckSummRead = USART_KESSEL_getchar();
33
  CheckSummRead = ( CheckSummRead << 8 ) + USART_KESSEL_getchar();
34
35
  if(CheckSummRead == CheckSummCalc)
36
  {
37
    return 1;
38
  }else
39
  {
40
    return 0;
41
  }
42
}
43
44
45
char temp_kommando_kessel(void)
46
{
47
  int i;
48
  unsigned char kommando[8];
49
  kommando[0] = 0x52;
50
  kommando[1] = 0x62;
51
  kommando[2] = 0x03;
52
  kommando[3] = 0x00;
53
  kommando[4] = 0x00;
54
  kommando[5] = 0x01;
55
  kommando[6] = 0x00;
56
  kommando[7] = 0xB8;
57
  for( i = 0; i < 9; i++)
58
  {
59
    USART_KESSEL_Transmit(kommando[i]);
60
  }
61
  return 1;
62
}


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.

von Karl H. (kbuchegg)


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

von Karl H. (kbuchegg)


Lesenswert?

Die hier
1
char temp_kommando_kessel(void)
2
{
3
  int i;
4
  unsigned char kommando[8];
5
  kommando[0] = 0x52;
6
  kommando[1] = 0x62;
7
  kommando[2] = 0x03;
8
  kommando[3] = 0x00;
9
  kommando[4] = 0x00;
10
  kommando[5] = 0x01;
11
  kommando[6] = 0x00;
12
  kommando[7] = 0xB8;
13
  for( i = 0; i < 9; i++)
14
  {
15
    USART_KESSEL_Transmit(kommando[i]);
16
  }
17
  return 1;
18
}

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.
1
char temp_kommando_kessel(void)
2
{
3
  int i;
4
  unsigned char kommando[] = { 0x52, 0x62, 0x03, 0x00, 0x00, 0x01, 0x00, 0xB8 };
5
6
  for( i = 0; i < sizeof( kommando ) / sizeof( *kommando ); i++ )
7
  {
8
    USART_KESSEL_Transmit(kommando[i]);
9
  }
10
  return 1;
11
}

von Stefan E. (sternst)


Lesenswert?

Sorry, aber deine ganze Grundstruktur ist schon etwas verquer.
1
ISR(USART0_RXC_vect)
2
{
3
receiveflag = USART0_readcommand(nutzdaten, steuerdaten, UDR0);
4
...
5
6
int USART0_readcommand(unsigned char *nutzdaten, unsigned char *steuerdaten, char start)
7
{
8
...
9
  for( i = 0; i < Len; i++ )
10
  {
11
    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.

von Chris T. (chris0086)


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.

von ozo (Gast)


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

von Chris T. (chris0086)


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...

von ozo (Gast)


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

von Chris T. (chris0086)


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?

von Stefan E. (sternst)


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.

von spess53 (Gast)


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

von Chris T. (chris0086)


Lesenswert?

Also ich habs jetzt nochmal probiert.
Leider sind die Ergebnisse ernüchternd.
Hier mal mein Code:
1
int Telegrammauswertung(unsigned char *nutzdaten)
2
{
3
  extern volatile int length;
4
  extern volatile int arrayplace;
5
  extern volatile char lengthflag;
6
7
  if(arrayplace == 2)
8
  {
9
    length = nutzdaten[2];
10
    lengthflag = 1;
11
    USART_KESSEL_Transmit(length);
12
  }
13
  if(arrayplace == (length+5) && lengthflag == 1)
14
  {
15
    int i;
16
    char checksummcalc= 0, checksummread;
17
    for(i=0;i<length+3;i++)
18
    {
19
      checksummcalc += nutzdaten[i];
20
    }
21
    checksummread = (nutzdaten[length+4] << 8) + nutzdaten[length+5];
22
    arrayplace = 0;
23
    lengthflag = 0;
24
    if (checksummread == checksummcalc)
25
    {
26
27
      return 1;
28
    }else return 0;
29
30
  }
31
}

in der Main
1
#define Takt 7372800
2
#define BAUD1 9600        // Baudrate
3
4
#define UBRR_VAL1 ((Takt/16/BAUD1)-1)   // Schnittstelle BUS
5
6
#define BAUD2 9600          // Baudrate
7
#define UBRR_VAL2 ((Takt/16/BAUD2)-1)   // Schnittstelle KESSEL
8
9
volatile unsigned char nutzdaten[60],steuerdaten[2];
10
volatile unsigned char receiveflag = 0, lengthflag = 0, temprequest = 0;
11
volatile int arrayplace = 0, length = 0;
12
13
ISR(USART0_RXC_vect)
14
{
15
16
17
nutzdaten[arrayplace]=UDR0;
18
//USART_KESSEL_Transmit(arrayplace);
19
arrayplace++;
20
21
};
22
23
24
25
int main(void)
26
{
27
UCSR0A |= (1<<RXC0);
28
UCSR0B |= (1<<TXEN0);                // USART1 TX einschalten
29
UCSR1B |= (1<<TXEN1);                // USART2 TX einschalten
30
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0);
31
UCSR1B |= (1<<RXEN1);
32
UCSR0C |= (1<<URSEL0)|(3<<UCSZ00);    // USART1 Asynchron 8N1
33
UCSR1C |= (1<<URSEL1)|(3<<UCSZ10);    // USART2 Asynchron 8N1
34
35
UBRR0H = UBRR_VAL1 >> 8;
36
UBRR1H = UBRR_VAL2 >> 8;
37
UBRR0L = UBRR_VAL1;
38
UBRR1L = UBRR_VAL2 ;
39
40
while (!bit_is_set(UCSR0A, UDRE0));
41
42
    DDRA = (0 << DDA7); // Pin  A7 als Eingang definieren
43
    initialisierung();
44
    sei();
45
46
47
do
48
{
49
  receiveflag = Telegrammauswertung(nutzdaten);
50
51
}while(1);
52
while(1);
53
}

Wenn ich jetzt folgenden String sende:


52610300000100B7

sollte ich von der Routine Telegrammauswertung eigentlich  nur einmal 
ein Zeichen zurück bekommen wegen:
1
if(arrayplace == 2)
2
  {
3
    length = nutzdaten[2];
4
    lengthflag = 1;
5
    USART_KESSEL_Transmit(length);
6
  }
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?

von Falk B. (falk)


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

von Chris T. (chris0086)


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?

von Karl H. (kbuchegg)


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
1
#define COMMAND_BYTE_1   1
2
#define COMMAND_BYTE_2   2
3
#define LENGTH_BYTE      3
4
#define DATA_BYTES       4
5
#define CHECKSUM_BYTE_1  5
6
#define CHECKSUM_BYTE_2  6
7
8
...
9
10
  uint16_t command;
11
  uint8_t  length;
12
  uint8_t  dataLength;
13
  uint8_t  data[30];    // genug für das längste Kommando
14
  uint16_t checksum;
15
  unsigned int c;
16
  uint8_t  byte;
17
18
  state = COMMAND_BYTE_1;    // das nächste Byte das reinkommt, ist das
19
                             // erste Byte des Kommandos
20
21
  while( 1 ) {
22
23
    c = uart_getc();
24
    if( ( c & 0xFF00 ) == 0 ) {    // wurde etwas empfangen?
25
      byte = c;                    // High Byte strippen, nur das Datenbyte bleibt
26
27
                                   // je nachdem welche Teile das Protokolls bis jetzt
28
                                   // abgearbeitet wurden, das Byte in die richtigen
29
                                   // Kanäle bringen  
30
      switch( state ) {
31
        case COMMAND_BYTE_1:          // das erste Byte des Kommandos ist da
32
          command = byte << 8;        // speichern
33
          state = COMMAND_BYTE_2;     // das nächste Byte wird das 2. Byte vom Kommando sein
34
          break;
35
36
        case COMMAND_BYTE_2:          // das 2.te Byte vom Kommando ist angekommen
37
          command |= byte;
38
          state = LENGTH_BYTE;
39
          break;
40
41
        case LENGTH_BYTE:             // Auch das LENGTH Byte ist schon da
42
          length = 0;
43
          dataLength = byte;
44
          state = DATA_BYTES;
45
          break;
46
47
        case DATA_BYTES:             // Soviele Datenbytes wie im Length Byte angekündigt empfangen
48
          data[ length++ ] = byte;
49
          if( length == dataLength )
50
            state = CHECKSUM_BYTE_1;
51
          break;
52
53
        case CHECKSUM_BYTE_1:        // erster Teil der Checksumme
54
          checksum = byte << 8;
55
          state = CHECKSUM_BYTE_2;
56
          break;
57
58
        case CHECKSUM_BYTE_2:        // zweites Byte der Checksumme
59
          checksum |= byte;
60
61
          // hier ist jetzt das Datenpaket vollständig
62
          // und wird ausgewertet
63
64
          ....
65
66
          // nach der Auswertung gehts wieder weiter, indem auf das erste
67
          // Kommandobyte gewartet wird
68
          state = COMMAND_BYTE_1;
69
          break;
70
      }
71
    }
72
  }

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.
1
uint8_t appendByte( uint8_t byte )
2
{
3
  uint8_t isComplete = 0;
4
  static uint8_t state = COMMAND_BYTE_1;
5
6
  switch( state ) {
7
    case COMMAND_BYTE_1:          // das erste Byte des Kommandos ist da
8
      command = byte << 8;        // speichern
9
      state = COMMAND_BYTE_2;     // das nächste Byte wird das 2. Byte vom Kommando sein
10
      break;
11
12
    case COMMAND_BYTE_2:          // das 2.te Byte vom Kommando ist angekommen
13
      command |= byte;
14
      state = LENGTH_BYTE;
15
      break;
16
17
    case LENGTH_BYTE:             // Auch das LENGTH Byte ist schon da
18
      length = 0;
19
      dataLength = byte;
20
      state = DATA_BYTES;
21
      break;
22
23
    case DATA_BYTES:             // Soviele Datenbytes wie im Length Byte angekündigt empfangen
24
      data[ length++ ] = byte;
25
      if( length == dataLength )
26
        state = CHECKSUM_BYTE_1;
27
      break;
28
29
    case CHECKSUM_BYTE_1:        // erster Teil der Checksumme
30
      checksum = byte << 8;
31
      state = CHECKSUM_BYTE_2;
32
      break;
33
34
    case CHECKSUM_BYTE_2:        // zweites Byte der Checksumme
35
      checksum |= byte;
36
      isComplete = 1;
37
      state = COMMAND_BYTE_1;
38
      break;
39
  }
40
41
  return isComplete ;
42
}
43
44
...
45
  while( 1 ) {
46
47
    c = uart_getc();
48
    if( ( c & 0xFF00 ) == 0 ) {    // wurde etwas empfangen?
49
      if( appendByte( c ) ) {
50
        .....
51
        hier die Auswertung des Datenpakets
52
        .....
53
      }
54
    }
55
  }

von Stefan E. (sternst)


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:
1
    int i;
2
    char checksummcalc= 0, checksummread;
3
    for(i=0;i<length+3;i++)
4
    {
5
      checksummcalc += nutzdaten[i];
6
    }
7
    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.

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.