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
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.
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
chartemp_kommando_kessel(void)
2
{
3
inti;
4
unsignedcharkommando[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
return1;
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.
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!
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.
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
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.
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
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.
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.
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.
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
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...
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
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?
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.
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
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?
@ 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
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?
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_tcommand;
11
uint8_tlength;
12
uint8_tdataLength;
13
uint8_tdata[30];// genug für das längste Kommando
14
uint16_tchecksum;
15
unsignedintc;
16
uint8_tbyte;
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
caseCOMMAND_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
caseCOMMAND_BYTE_2:// das 2.te Byte vom Kommando ist angekommen
37
command|=byte;
38
state=LENGTH_BYTE;
39
break;
40
41
caseLENGTH_BYTE:// Auch das LENGTH Byte ist schon da
42
length=0;
43
dataLength=byte;
44
state=DATA_BYTES;
45
break;
46
47
caseDATA_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
caseCHECKSUM_BYTE_1:// erster Teil der Checksumme
54
checksum=byte<<8;
55
state=CHECKSUM_BYTE_2;
56
break;
57
58
caseCHECKSUM_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_tappendByte(uint8_tbyte)
2
{
3
uint8_tisComplete=0;
4
staticuint8_tstate=COMMAND_BYTE_1;
5
6
switch(state){
7
caseCOMMAND_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
caseCOMMAND_BYTE_2:// das 2.te Byte vom Kommando ist angekommen
13
command|=byte;
14
state=LENGTH_BYTE;
15
break;
16
17
caseLENGTH_BYTE:// Auch das LENGTH Byte ist schon da
18
length=0;
19
dataLength=byte;
20
state=DATA_BYTES;
21
break;
22
23
caseDATA_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
caseCHECKSUM_BYTE_1:// erster Teil der Checksumme
30
checksum=byte<<8;
31
state=CHECKSUM_BYTE_2;
32
break;
33
34
caseCHECKSUM_BYTE_2:// zweites Byte der Checksumme
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:
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.