Hallo zusammen!
Ich messe mit dem ICP eine Frequenz (eigentlich die Zeit zwischen zwei
Impulsen, was funktioniert), die ich anschließend an einen anderen
Atmega verschicken will.
Mein Problem ist, dass die Frequenz in einem regelmäßigen Abstand von
ca. 1ms vom Empfänger-Controller "angefordert" werden soll.
Wenn also ein Impuls vom Empfänger! kommt (INT0 am Sender wird high
gesetzt -Interrupt ausglöst), soll er das Frequenzmessen sein lassen und
sofort! die letzte gemessene Frequenz verschicken.
Ich möchte damit vermeiden, dass nicht nach jeder Frequenzmessung das
Ergebnis verschickt wird sondern nur wenn Pin D2 high gesetzt wird, so
dass meine Übertragung nicht mehr abhängig von der Dauer der zwei
IC-Impulse ist.
Der auskommentierte Bereich in der main funktioniert, ist aber genau das
was ich vermeiden will!
1
#include<stdlib.h>
2
#include<stdio.h>
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/interrupt.h>
6
7
#ifndef F_CPU
8
#define F_CPU 16000000
9
#endif
10
11
volatileunsignedintoverflow=0;
12
volatileunsignedlongic_zp_A=0;// ICR-Wert bei 1.High-Flanke speichern
13
volatileunsignedlongic_zp_B=0;// ICR-Wert bei 2.High-Flanke speichern
14
volatileunsignedcharUpdateUART;//Job Flag
15
volatileuint8_tsend=0;
16
unsignedchari=0;
17
uint32_tzw_Erg=0;
18
uint32_tErg=0;
19
20
ISR(TIMER1_CAPT_vect)
21
{
22
if(UpdateUART==1)//auf vorherige Messung warten
23
return;
24
25
if(i==0)// 1.High Flanke
26
{
27
ic_zp_A=ICR1;
28
overflow=0;
29
i=1;
30
}
31
else// 2.High Flanke
32
{
33
ic_zp_B=ICR1;
34
UpdateUART=1;
35
i=0;
36
}
37
}
38
39
ISR(TIMER1_OVF_vect)
40
{
41
overflow++;
42
}
43
44
ISR(INT0_vect)//*******hier ist das Problem********
45
{
46
while(!(UCSRA&(1<<UDRE)))// warten bis Senden moeglich
47
{
48
}
49
UDR=send;// sende Zeichen
50
}
51
52
intmain(void)
53
{
54
TCCR1B=(1<<ICES1)|(1<<CS10);// Input Capture Edge, kein PreScale ->Taktfrequenz: 16MHz externer Quarz
Erstens hast du wohl vergessen, dein Problem zu beschreiben, was du
überhaupt damit hast. Den benutzten Prozessor dazuzuschreiben, könnte
auch hilfreich sein.
Zweitens glaube ich dir einfach mal gar nicht, dass der
auskommentierte Bereich auch nur ansatzweise funktioniert: es gibt
nirgends in dem Code eine Definition einer Variablen namens "Send".
Falls das Ganze ein Schreibfehler sein soll (was hast du dann beim
Abtippen noch alles verschrieben?) und "send" gemeint ist: wie kann es
funktioniert haben, wenn send doch eine 8-bit-Variable ist, du aber
ein "low- und high-Byte" von dieser ausgeben willst?
>ISR(INT0_vect) //*******hier ist das Problem********>{> while (!(UCSRA & (1<<UDRE))) // warten bis Senden moeglich> {> }> UDR = send; // sende Zeichen>}
Oh ja das ist ein Problem.
Warum versuchen Leute immer, als wenn sie in Basic wären, aus einer
Interruptroutine zu senden. Bist du bewußt was du da hingeschrieben
hast???
-> warte bis ich wieder senden darf; bei einer UART
Datenrate 9600 -> ein Zeichen dauert 1ms
Datenrate 115200 -> 86 us
Datenrate 250000 -> 40 us
In dieser Zeit kannst du nichts tun, alle interrupts sind gesperrt!
Du willst den Controller doch noch zu was anderen als nur der Messung
benutzen?
Überlege es dir doch mal selbst, effektiv wilst du in 1ms 1 Zeichen
senden.
Aufgrund deines Konzepts bist du genötigt die Datenrate so hoch zu
nehmen,
daß du in der Zeit 25 Zeichen senden könntest. Da sollte doch langsam
klar werden das hier konzeptionell was nicht stimmt.
Setze das alles in die main Schleife und sende mit TX(UDRE) interrupt
und Puffer. Dann wird nämlich das was du jetzt machst nur noch <1% Last
bedeuten.
und du hast auch kein Problem 16 oder 32 Bit zu senden
Du schreibst im Kommentar das du 16 Bit senden willst, sendest aber nur
8Bit???
Den auskommentierten Bereich am besten ignorieren den hab ich noch aus
nem anderen Programm reinkopiert. War nur gedacht als Gegenbeispiel wie
ich's nicht machen will.
Ist ein Atmega16 mit 16MHz.
Das Problem ist, dass er nichts verschickt und ich nicht weiß ob das
überhaupt so funktionieren könnte.(In einer ISR über UART was
rausschicken).
Gibt es Alternativen zu meinem Versuch hier?
Denn wenn ich die Daten in der main über Polling verschicke dann dauert
das immer viel länger als die eigentliche Dauer für die Frequenzmessung
und ist zudem noch Frequenz abhänig.
Ein Beispiel: gemessene Frequenz 100Hz -> dauer zwischen zwei Impulsen
0,01s -> am Empfänger die UART Übertragung gemessen: 4ms!
woran liegt das?
Deshalb will ich die Sache mit dem externen Interrupt lösen, oder wie
würdet/st ihr/du das machen?
Das geht schief.
Zwar nur bei bestimmten Frequenzen, dann aber gründlich.
Du mußt zu jedem Capture den Overflowzähler speichern und erstmal
feststellen, ob er vor oder hinter dem Capture kam.
Sonst hast Du manchmal 65536 Zyklen Meßfehler.
Auch sollte man ne maximale Zyklenzahl definieren, ab der eine zu
geringe Frequenz anliegt und dann den Wert 0Hz ausgeben.
Peter
>oder wie würdet/st ihr/du das machen?
nimm den auskommentierten Bereich wieder rein. Genau so mußt du es
machen
nur uart_putc darf nicht mit Polling arbeiten sondern muß mit
Zwischenpuffer und UDRE Interrupt arbeiten! Das Senden läuft dann
parallel zur Messung.
@Wolfram:
das eignetliche Problem ist, dass ich die Frequenz nur ca. jede 1ms am
Empfänger benötige, dann aber sofort (deshalb die hohe Baudrate)(wird
für eine Regelung verwendet). Sprich der Empfänger soll sich zu
bestimmten Zeiten die Frequenz holen und dann möglichst schnell.
Zuerst wollte ich das mit mit 8bit versuchen später sollen Frequenzen
bis 65kHz übertragen werden, also 16bit.
Ich hab den UART Interrupt nicht ganz verstanden. Du meinst doch den TX
complete interrupt. Wie soll ich den verwenden, ich will ja nichts
machen wenn der USRT fertig ist sonder er soll senden wenn ein Signal
vom Empfänger kommt.
Grüße
Ah, der UDRE Interrupt.
Das heißt der Puffer wird gefüllt und erst dann wenn vom Empfänger ein
Signal kommt verschickt?
Das wäre die Lösung werds probieren.
Holger Brenner wrote:
> Das Problem ist, dass er nichts verschickt und ich nicht weiß ob das> überhaupt so funktionieren könnte.(In einer ISR über UART was> rausschicken).> Gibt es Alternativen zu meinem Versuch hier?
Die grundsärtzliche Idee mit dem INT0 ist ja nicht
schlecht. Nur darfst (sollst) du nicht in der ISR die
Daten verschicken.
Mach doch das selbe Spielchen, dass du mit dem
UpadateUart Job Flag gemacht hast einfach nochmal:
In der ISR wird eine globale Variable (das Jobflag)
auf 1 gesetzt.
in der main() wird neben all den anderen Dingen auch
dieses Jobflag noch überprüft und wenn es 1 ist, dann
wird über die UART verschickt.
int main(void)
{
...
for( ; ; )
{
if( UpdateUART == 1 )
{
...
UpdateUART = 0;
}
if( SendUART == 1 )
{
...
SendUART = 0;
}
}
return 0;
}
und die ISR setzt ganz einfach SendUART auf 1
Wenn das immer noch nicht reicht, dann muss man die
Verschickerei selbst wieder über einen Interrupt machen:
Man stellt das Zeichen in UDR und die UART benachrichtigt einen
mit einem Interrupt wenn es fertig verschickt wurde und
das nächste Zeichen verschickt werden kann.
>das eignetliche Problem ist, dass ich die Frequenz nur ca. jede 1ms am>Empfänger benötige, dann aber sofort
Du stellst Konzepte auf die dich in Zeitliche Abhängigkeiten führen die
nur sehr umständliche zu lösen sind. Dies innerhalb eines Programmes zu
tun vielleicht, aber zwischen 2 Mikrocontrollern auf keinen Fall.
Ich vermute sehr du machst es so, dein Master-Mikrocontroller setzt
einen Pin
und dein Meßmikrocontroller soll antworten. Richtig?
Gibt es auf der UART verbindung noch irgendwelche anderen
Mikrocontroller?
Kann es sein, daß du das ganze nur mit 2 Mikrocontrollern löst, weil du
Probleme hattest es zeitlich in einen zu integrieren?
>Das heißt der Puffer wird gefüllt und erst dann wenn vom Empfänger ein>Signal kommt verschickt?
Nein, das heißt das letzte Byte wird gerade gesendet, du kannst ein
weiteres zum Senden (wenn du es hast) an die UART geben.
@Peter:
werd ich gleich mal versuchen und meld mich dann.
@Wolfram:
Genau das ist das Problem.
Es gibt noch zwei weitere MeßController die nacheinander Daten zum
MainController schicken sollen, immer mit dem gleichen Prinzip: Pin high
setzen Controller schickt, nächster Pin high setzen nächster Controller
schickt,...
Will eigentlich nicht von diesem Konzept abweichen, aber wenn du ne
einfacher Lösung parat hast lass ich mich gern überzeugen.
sofort! ist entweder unmöglich, oder aber sehr relativ. Was meinst du
damit? Warum benötigt die Regelung eine Abtatszeit von 1ms? Was
passiert, wenn der Frequenzwert bis zu einer Millisekunde "alt" ist?
Stört die Totzeit? Was passiert bei Frequenzen unter 1kHz (da dann die
Messung länger als 1ms dauert...)? Warum überhaupt 2 Mikrocontroller?
Wenn es wirklich schnell gehen soll mit der Übertragung, warum dann
uart, und nicht 8-bit (oder gleich 16) parallel? Oder SPI? ODer TWI?
???
Oliver
Konzept:
Kontinuierliche Messung Zeit zwischen den High Flanken im Interrupt
aber: bei der 2. ten Flanke wird Erg berechnet und alles zurück gesetzt
Eigentlich müßtest du wenn du Erg zuweist bei >8Bit kurz den Interrupts
verbieten
Int0:
Du übernimmst Erg in einen Puffer(16..32Bit) und sendest diesen per
UartInterrupt Im Int0 wird nur das Senden für das erste zeichen
angestoßen
Die restlichen Zeichen werden jeweils in dem UDRE Interrupt nachgefüllt.
Für das letzte Zeichen nicht vergessen den UDRE Interrupt wieder
auszuschalten
main leere Schleife
Baudrate für die UART kann ruhig 250KBaud sein
@ Karl Heinz:
das sieht ganz gut aus. Die Zeitdauer fürs Empfangen geht nicht über die
1ms drüber, wenn man das so messen kann?:
am Empfänger:
PORTC |= (1<<PC6);
a = uart_getc();
PORTC &= ~(1<<PC6);
und dann am Oszi den Pin anschauen.
@ Wolfram:
das mit dem INT0 versteh ich noch nicht ganz. Wie kann ich im INT0 den
UART anstoßen? Meinst du ein Flag setzen für die main?
nein ,erlaube UDREInt wenn du weitere zeichen aus dem Puffer senden
willst
und sende das erste Zeichen aus dem Puffer
(Es kann kein anderes Zeichen aktuell von der UART gesendet werden, da
du per Definition gesagt hast das erst bei INT0 gesendet wird und auch
indirekt voraussetzt daß die letzte Sendung schon beendet ist.)
Holger Brenner wrote:
> @ Karl Heinz:> das sieht ganz gut aus.
Vergiss es wieder.
Der Vorschlag von Wolfram ist besser.
> @ Wolfram:> das mit dem INT0 versteh ich noch nicht ganz. Wie kann ich im INT0 den> UART anstoßen? Meinst du ein Flag setzen für die main?
Einfach das erste Zeichen in UDR schreiben, wenn UDR
frei ist (sollte es aber sein).
Die weiteren Zeichen holt sich dann der UART per Interrupt
selbst ab.
Da ich erst am Amfang vom C-Programmieren bin, könnt ihr mir dazu noch
ein bisschen mehr erzählen?
Was genau passiert in INT0:
Zuerst wird "erg" in einer Variablen abgelegt.
Dann wird der Data Register Empty Interrrupt freigegeben: (UCSRB |=
(1<<UDRIE) ) so dass der UART Interrupt anfängt Daten zu verschicken.
Der Rest geht dann von allein? Muss ich den UART Interrupt wieder
abschalten wenn ich nicht's senden will?
Was macht meine Frequenzmessung so lange? Passiert das quasi parallel?
Wie funktioniert das dann auf der Empfängerseite?
Dort wird über einen Timer alle 1ms der Pin high gesetzt, aber in einer
ISR kann ich die Daten ja nicht empfangen, also auch beim Empfangen mit
dem UART Interrupt arbeiten?
hab ich schon gemacht werd's aber nochmal genauer studieren.
Die Sache mit dem UART ist halt nicht so leicht zu verstehen zumal man
verstehen muss was auf beiden Seiten passiert.
Ich muss eine zehntausender Zahl verschicken also zwei 8bit-Zeichen und
dann wieder zusammen setzen.
Ich meld mich nochmal wenn ich besser durchblick.
Holger Brenner wrote:
> Was genau passiert in INT0:> Zuerst wird "erg" in einer Variablen abgelegt.> Dann wird der Data Register Empty Interrrupt freigegeben: (UCSRB |=> (1<<UDRIE) ) so dass der UART Interrupt anfängt Daten zu verschicken.
Nein. So funktioniert das nicht.
Nur weil ein Interrupt freigegeben ist, sendet der UART
noch lange nichts.
Ein Interrupt tritt immer als Reaktion auf ein Ereignis
auf. Welches ist das Ereignis? Das Ereignis ist, dass
die UART Hardware das übergebene Zeichen soweit raus-
geschickt hat, dass es das nächste Zeichen annehmen könnte.
Daraus folgt: Um den Prozess in Gang zu bringen muss man
das erste Zeichen in die UART hineinstecken (in Form von:
ins UDR Register schreiben). Die UART rappelt dann vor sich
hin und wenn sie soweit ist, kommt ein Interrupt. In der
Interrupt Routine wird dann das nächste Zeichen in die
UART hineingestopft. Die UART rappelt wieder und wenn sie
soweit ist, generiert sie den nächsten Interrupt. In der
Interrupt Routine wird das nächste Zeichen in die UART
gesteckt ... wie, da ist kein Zeichen mehr? Na wenn da
kein Zeichen mehr ist, dann macht die ISR halt gar nichts
und stopft kein neues Zeichen in die UART, sodass die UART
dann auch zur Ruhe kommt.
> Der Rest geht dann von allein? Muss ich den UART Interrupt wieder> abschalten wenn ich nicht's senden will?
Nein. Du gibst der UART einfach kein Zeichen mehr und gut ists.
> Was macht meine Frequenzmessung so lange? Passiert das quasi parallel?
Ja.
Die UART arbeitet ganz von alleine und das bischen Zeichenschubserei
in der Interrupt Routine macht der Prozessor mit links.
>> Wie funktioniert das dann auf der Empfängerseite?> Dort wird über einen Timer alle 1ms der Pin high gesetzt, aber in einer> ISR kann ich die Daten ja nicht empfangen, also auch beim Empfangen mit> dem UART Interrupt arbeiten?
Das ist wieder eine ganz andere Geschichte.
Auf der Empfangsseite kannst du auch mit Interrupts arbeiten.
Die UART empfängt ein Zeichen ganz von alleine. Wenn es eines
hat (Achtung: da ist es wieder. Ein Ereignis ist eingetreten,
ein Zeichen wurde empfangen), dann passiert was: Genau
ein Interrupt wird ausgelöst.
Eventuell wäre es sinnvoll, du legst dein Projekt mal
zur Seite und schaust dir in einem Testprojekt einfach mal
an, welche Möglichkeiten man mit dem UART hat.
Ich widerspreche Karl Heinz ja selten, aber...
Karl heinz Buchegger wrote:
> Ein Interrupt tritt immer als Reaktion auf ein Ereignis auf. Welches> ist das Ereignis? Das Ereignis ist, dass die UART Hardware das> übergebene Zeichen soweit rausgeschickt hat, dass es das nächste> Zeichen annehmen könnte.
Dieses ,Ereignis' ist aber praktisch immer präsent, wenn die UART
nichts zu tun hat: dann ist ihr (Sende-)Datenregister nämlich immer
leer. Damit genügt also das bloße Aktivieren dieses Interrupts, um
wenige Takte später gleich einen IRQ zu bekommen.
>> ... Muss ich den UART Interrupt wieder>> abschalten wenn ich nicht's senden will?> Nein.
Doch, muss er in diesem Falle. Wenn man nichts mehr zu senden hat,
muss man den UDRE-Interrupt wieder ausschalten, sonst triggert er
(aus eben beschriebenem Grunde) immer wieder.
Ob man nun das erste Zeichen in die UART reinstopft, bevor man den
UDRE-Interrupt freigibt oder es gleich der ISR überlässt, dieses
Zeichen auszugeben, ist eher nebensächlich.
Jörg Wunsch wrote:
> Ich widerspreche Karl Heinz ja selten, aber...
Tus ruhig :-)
>> Karl heinz Buchegger wrote:>>> Ein Interrupt tritt immer als Reaktion auf ein Ereignis auf. Welches>> ist das Ereignis? Das Ereignis ist, dass die UART Hardware das>> übergebene Zeichen soweit rausgeschickt hat, dass es das nächste>> Zeichen annehmen könnte.>> Dieses ,Ereignis' ist aber praktisch immer präsent, wenn die UART> nichts zu tun hat: dann ist ihr (Sende-)Datenregister nämlich immer> leer. Damit genügt also das bloße Aktivieren dieses Interrupts, um> wenige Takte später gleich einen IRQ zu bekommen.
Ich hatte eigentlich eher den TX Complete Interrupt im Auge. Ich
wusste aber nicht mehr wann der Int. jetzt exakt auftritt, daher
die eher nebulöse Beschreibung da oben. War wohl etwas misverständlich
formuliert.
Der Vorteil von UDRE statt TXC ist, dass man bereits wieder Daten in
das tx buffer register schreiben kann, während die aktuellen Daten
gerade rausgeschoben werden. Dadurch kann man eine "Rücken an
Rücken"-Übertragung realisieren, d. h. nach dem Stop-Bit folgt
wirklich sofort ein Startbit.
Wenn man TXC statt UDRE nimmt, stimmt Karl Heinz' Beschreibung
100%ig.