Hi, ich bin neu hier im Forum und habe ein Problemchen.
Ich nutze einen ATMEGA32A und einen Profibus DP Chip von
Deutschmann(UNIGATE IC-PROFIBUS DP)
Diese kommunizieren via USART - völlig problemlos.
Baud: 250k
Ich habe eine ISR, welche die USART-Kommunikation festlegt:
ISR(USART_RXC_vect)
Nun habe ich zusätzlich den PINC,0 an meinem ATMEGA32A als Eingang
definiert und empfange dort von einem anderen Mircocontroller ein paar
Bits.
Aufgerufen wird dieser Daten-Empfang in der ISR(TIMER2_COMP_vect).
Auch das funktioniert super - solange keine Profibus kommunikation
stattfindet.
Werden Profibus-Werte übermittelt, bekomme ich in meinem Datenempfang
regelmäßig falsche Werte.
Meine Vermutung ist, dass die ISR für USART zu lange dauert.
Im Moment übertrage ich 80 Bytes am Stück:
1
unsignedchari,empf_byte[80];
2
3
if(UDR!='A')return0;//Anfang
4
for(i=1;i<=79;i++){while(!(UCSRA&(1<<RXC)));empf_byte[i]=UDR;}//warten und abholen Zeichen
5
if(empf_byte[79]!='E')return0;//Ende
6
for(i=1;i<=78;i++)Variable[i]=empf_byte[i];
7
return1;
Nun habe ich Zwei Ideen zur Lösung des Problemes:
1)nicht mehr alles auf einmalschicken, sondern "Byteweises senden"
Ein Byte senden -> irgendwas tun -> nächstes Byte senden etc
2)USART ISR durch eine weitere ISR unterbrechen.
Mein Problem ist jedoch, das ich nicht so recht weis, wie ich das
geschickt realisieren kann.
Hat hierzu eventuell jmd einen Rat oder Tipps für mich ?
Danke schon einmal im Voraus :)
Ben
--
Wenn Du statt der "avrasm"-Tags die für C-Quelltext vorgesehenen
"c"-Tags verwendest, sieht das Syntax-Highlighting doch gleich viel
besser aus, oder?
-rufus
Ben B. schrieb:> Auch das funktioniert super - solange keine Profibus kommunikation> stattfindet.> Werden Profibus-Werte übermittelt, bekomme ich in meinem Datenempfang> regelmäßig falsche Werte.
Dann darfst Du offensichtlich nur sehr kurze Interrupts verwenden.
Ben B. schrieb:> Meine Vermutung ist, dass die ISR für USART zu lange dauert.> Im Moment übertrage ich 80 Bytes am Stück:
Haste mal ausgerechnet wie lange das bei 250kBaud dauert..? Hint: Ein
Byte über UART versendet sind mindestens 10 Bits.
Byteweise senden könnte klappen, muss aber nicht. Wir wissen nämlich
nicht wie empfindlich dein Timer Compare sein muss.
Ben B. schrieb:> Mein Problem ist jedoch, das ich nicht so recht weis, wie ich das> geschickt realisieren kann.
dafür verwende man einen ringpuffer. Die Sendefunktion schieben es nur
in den Puffer und die ISR holt sich wenn UART frei ist, eine Zeichen
versendet das und das wars.
Fix und fertig ist z.b. die Fleury's UART library
Ist das Deine ISR-Routine?
unsigned char i, empf_byte[80];
if(UDR != 'A') return 0;//Anfang
for(i=1; i<=79; i++) {while(!(UCSRA & (1<<RXC))); empf_byte[i]=UDR;}
//warten und abholen Zeichen
if(empf_byte[79] != 'E') return 0;//Ende
for(i=1; i<=78; i++) Variable[i] = empf_byte[i];
return 1;
Das hiesse, Du löst mit dem ersten Zeichen einen Interrupt aus und
wartest dann solange innerhalb der ISR-Routine, bis 80 Zeichen empfangen
wurden. Das ist ein etwas unorthodoxes Vorgehen ...
Sinnvoller baust Du die ISR-Routine so um, dass Du sie nach dem Empfang
eines Zeichens sofort wieder verlassen kannst. Dazu brauchst Du neben
Deinem Buffer noch mindestens eine diesem Buffer zugeordnete globale
Zählervariable. Mit dieser stellst Du beim jedem Start der ISR-Routine
fest, an welcher Stelle Deines Empfangs-Streams Du Dich befindest.
Viele Grüße, Stefan
> for(i=1; i<=79; i++) {while(!(UCSRA & (1<<RXC))); empf_byte[i]=UDR;} //warten
und abholen Zeichen
In einer Interruptroutine ... wow, sowas von dreist. In einer Interrupt
Routine wird sicher gar nie gewartet. Schreib das 100 mal ab und dann
mach's richtig.
Danke schonmal für die ganzen Tipps schonmal, so langsam wird mir
einiges klarer.
Jim M. schrieb:> Haste mal ausgerechnet wie lange das bei 250kBaud dauert..? Hint: Ein> Byte über UART versendet sind mindestens 10 Bits.>> Byteweise senden könnte klappen, muss aber nicht. Wir wissen nämlich> nicht wie empfindlich dein Timer Compare sein muss.
1) die Dauer müsste dann quasi 3,2ms sein, wenn ich das richtig
gerechnet habe (800 Bits gesamt bei 250k bits/s)
2) Meine Empfang Abfrage(nicht UART) ist sehr wichtig und muss
eigentlich die höchste Priorität haben.
Peter II schrieb:> dafür verwende man einen ringpuffer. Die Sendefunktion schieben es nur> in den Puffer und die ISR holt sich wenn UART frei ist, eine Zeichen> versendet das und das wars.>> Fix und fertig ist z.b. die Fleury's UART library
Bei dem Wort Ringpuffer stellen sich bei mir alle Nackenhaare auf, da
habe ich ein regelrechtes Trauma aus dem Studium mitgenommen ;)
Karl M. schrieb:> arbeiten die beiden Uart als Hardware Uart und ist jeweis ein RX- und> TX- Fifo implementiert ?
Hi Karl, so vertraut bi nich leider noch nicht mit der Materie, könntest
du mir erklären was du damit meinst ? :)
Stefan K. schrieb:> Ist das Deine ISR-Routine?>> unsigned char i, empf_byte[80];>> if(UDR != 'A') return 0;//Anfang> for(i=1; i<=79; i++) {while(!(UCSRA & (1<<RXC))); empf_byte[i]=UDR;}> //warten und abholen Zeichen> if(empf_byte[79] != 'E') return 0;//Ende> for(i=1; i<=78; i++) Variable[i] = empf_byte[i];> return 1;>> Das hiesse, Du löst mit dem ersten Zeichen einen Interrupt aus und> wartest dann solange innerhalb der ISR-Routine, bis 80 Zeichen empfangen> wurden. Das ist ein etwas unorthodoxes Vorgehen ...>> Sinnvoller baust Du die ISR-Routine so um, dass Du sie nach dem Empfang> eines Zeichens sofort wieder verlassen kannst. Dazu brauchst Du neben> Deinem Buffer noch mindestens eine diesem Buffer zugeordnete globale> Zählervariable. Mit dieser stellst Du beim jedem Start der ISR-Routine> fest, an welcher Stelle Deines Empfangs-Streams Du Dich befindest.>> Viele Grüße, Stefan
Das ist quasi der Empfangsabschnitt des DP, welcher in der ISR abläuft.
hier mal die Komplette ISR:
1
ISR(USART_RXC_vect)
2
//ISR(USART_RXC_vect,ISR_NOBLOCK)
3
{
4
UCSRB&=~(1<<RXCIE);//Interrut beim Empfangenem Zeichen gespert
5
if(profibus_empf())
6
{
7
8
//profibus_send();
9
ON(M_SENDEN);
10
dp_tout=0;
11
}
12
elsedp_fehler++;
13
UCSRB|=(1<<RXCIE);//Interrut beim Empfangenem Zeichen frei
14
15
}
Der Sende-Teil wird mittels Flag(M_Senden) außerhalb der ISR gestartet.
Ich hatte mit das so überlegt:
In der ISR rufe ich zunächst den Empfangscode auf und zähle danach einen
Zähler hoch bis maximal auf 79. Ich möchte ein Zeichen empfangen und es
der entsprechenden Variablen (vom Zählerwert abhängig) zuordnen.
Quasi nach jedem Empfangen Zeichen, wird die ISR verlassen und findet
wieder Statt, sobald ein neues Zeichen ankommt.
Muss ich das gleiche dann auch für den Sendeabschnitt machen, obwohl der
außerhalb der ISR liegt?
Sapperlot W. schrieb:>> for(i=1; i<=79; i++) {while(!(UCSRA & (1<<RXC))); empf_byte[i]=UDR;} //warten> und abholen Zeichen>> In einer Interruptroutine ... wow, sowas von dreist. In einer Interrupt> Routine wird sicher gar nie gewartet. Schreib das 100 mal ab und dann> mach's richtig.
Das hat bis hierhin auch Problemlos funktioniert, da zuvor keine
Zeitkritischen Operationen stattfanden.
Ich habe ein bestehendes Projekt um eine zeitkritische Kommunikation
erweitert, dadurch muss ich diesen Abschnitt ja ändern. :)
Ben B. schrieb:> for(i=1; i<=78; i++) Variable[i] = empf_byte[i];
Das nennt sich memcpy:
1
#include<string.h>
2
memcpy(Variable,empf_byte,78);
Ben B. schrieb:> Bei dem Wort Ringpuffer stellen sich bei mir alle Nackenhaare auf, da> habe ich ein regelrechtes Trauma aus dem Studium mitgenommen ;)
Warum?
Jeder nimmt für die UART ne FIFO. Das ist kein Hexenwerk, nimmt Dir aber
zeitkritische Arbeit ab.
Du hast also ein Interrupt-Routine, beispiel void int_rx(void), die
Deinen geposteten Code (Beispiel int decode(void)) aufruft und nachher
ein flag setzt, wenn der mit 1 zurückkommt?
also z.B.
1
intfertig=0;
2
intdecode(void);
3
unsignedcharVariable[79];
4
5
INTERRUPTvoidint_rx(void)
6
{
7
if(!fertig)fertig=decode();
8
}
9
10
intdecode(void)
11
{
12
....
13
}
ja? Dann schreibe decode so um:
1
staticunsignedcharempf_byte[80];
2
staticunsignedchari=0;
3
staticunsignedcharrun=0;
4
unsigendcharc=UDR;
5
6
7
if(!run)
8
{
9
if(c=='A'){run=1;};
10
i=0;
11
/* warum speicherst Du das 'A' nicht ab? */
12
/* ah, verstehe... darum hier der Trick mit uchar c! */
13
return0;
14
}
15
i++;
16
if(i<79){empf_byte[i]=c;return0;}
17
/* Fehler ? Alles auf Anfang ? warum plenke ich ? */
18
if(c!='E')
19
{
20
run=0;
21
return0;
22
}
23
/* alles OK! schnell noch kopieren, bevor sich einer beschwert!! */
24
for(i=1;i<=78;i++)Variable[i]=empf_byte[i];
25
run=0;
26
return1;
27
}
Das ist zwar immer noch unschön, da
a) das Kopieren (grundlos) hier in der ISR ist,
b) zuviele komische Verschiebungen (+1, -1, <= ) stattfinden
c) würdest Du das erste Byte mit kopieren, wäre der Code viel sauberer
und kleiner
aber es
- wartet nicht im Interrupt,
- hat einen sehr einfachen Fifo für Fifotraumatisierte
und man kann ja immer nur einen schritt nach dem anderen gehen.
Den Du irgendwas Byteweise verschickst, musst Du doch sowieso nach jedem
Byte fragen: "Seid Ihr denn auch alle da"?
Im Normalfall ist an dieser Stelle ist dann Feierabend angesagt oder
weitermachen. Ist aber auch 'ne gute Stelle für 'ne Pinkelpause. Der
Zähler und die Zeiger, rund um den Puffer, sollten aktuell sein und eine
Unterbrechung somit kein Problem.
Ich vergaß!
Wenn die Programmstruktur nicht total daneben ist, sollte das Programm
folgendermaßen aussehen:
1. Eine Hintergrundroutine, die mit einem Ringpuffer zusammen arbeitet.
2. Eine Funktion zum "füttern" des Ringpuffers, die auch die Übertragung
startet.
Nach der Übergabe des aktuellen Bytes werden die Zeiger im Ringpuffer
aktualisiert. Dann wird überprüft ob der Puffer leer ist oder ein neu
einzufügendes Flag, für Unterbrechung, gesetzt ist.
An dieser Stelle erfolgt sowohl wenn der Puffer leer ist, als auch bei
einer Abbruchanforderung das gleiche.
Es gibt natürlich einen Sonderfall: Unterbrechung UND Puffer leer.
Da das Senden üblicherweise mit dem füttern des Puffers gestartet wird,
gibt es natürlich eine kleine Änderung.
Entweder Du schreibst einen neuen Wert in den Puffer, wobei dann an der
"letzten" Stelle weitergemacht wird oder Du baust, in die Hauptroutine,
eine Kombination wie:
Unterbrochen UND ProblemGeloest UND NochWasImPuffer: Starte USART ein.
Ein interessantes Problem ergibt sich aber, wenn die Unterbrechung
augenblicklich erfolgen muss. Die meisten USARTs schubsen das Byte,
welches sich gerade im Sendepuffer befindet bedingungslos raus.
Allerdings muss für diesen Sonderfall auch der Empfänger befummelt
werden.
Achim S. schrieb:> Das ist zwar immer noch unschön, da> a) das Kopieren (grundlos) hier in der ISR ist,> b) zuviele komische Verschiebungen (+1, -1, <= ) stattfinden> c) würdest Du das erste Byte mit kopieren, wäre der Code viel sauberer> und kleiner> aber es> - wartet nicht im Interrupt,> - hat einen sehr einfachen Fifo für Fifotraumatisierte> und man kann ja immer nur einen schritt nach dem anderen gehen.
Du bist meine Rettung, genau das hat mir schon geholfen !!!!!
Scheinbar läuft jetzt alles, werde mich am Montag nochmal intensiv damit
auseinander setzen.
Besten Dank schon einmal - auch an alle anderen, die mir Tipps gegeben
haben - und ein schönes Wochenende !
So sollte Unterstützung in einem Forum aussehen ! :)