Hi,
ich probiere mit einem ATMega128 eine Frequenz zu messen (rund 40Khz).
Der ATmega ist getaktet mit 14,7456Mhz. Dafür habe ich einen Interrupt
definiert, und das Signal an INT0 angelegt. Hier mein Code:
1
voidinit_interrupt(void){
2
DDRD=0x00;
3
MCUCR|=(1<<ISC00)|(1<<ISC01);
4
EICRA|=(1<<ISC01)|(1<<ISC00);
5
EIMSK|=(1<<INT0);
6
}
7
8
voidinit_timer(void){
9
TCCR1B|=(1<<CS10);
10
DDRB=0xff;
11
}
12
13
14
ISR(INT0_vect){
15
cli();
16
chars[7];
17
uint8_tstart=TCNT1;
18
uart_sendstring(itoa(start,s,10));
19
uart_sendchar('\n');
20
TCNT1=0;
21
sei();
22
}
Mein Problem ist, dass ich in einer relativ geringen Spanne um 40Khz
messen möchte, die Werte aber immer gleich sind, nämlich immer bei
51-52. Aber das verstehe ich eben nicht, denn mit 14,7456 müsste man
diese Frequenzen doch ganz gut messen können, oder? Oder seht Ihr
vielleicht meinen Fehler?
MfG, und vielen Dank schon einmal, Ozzy
Ohne jetzt die ganzen Timer und Interrupt Einstellungen
kontrolliert zu haben:
Wie denkst du über Folgendes:
Bei 40Khz kommt ein Interrupt in welchen Zeitabständen?
Wie lange dauert es wohl um einen itoa und eine komplette
UART Übertragung zu machen?
Hi,
deshalb deaktiviere ich doch die Interrupts, während ich per UART sende.
Erst wenn ich das gemacht habe, setze ich den Timer wieder auf null und
aktiviere die Interrupts. Wenn ich dann einige Messungen verpasse, ist
das (relativ) egal, da ich weiß, dass das Signal lange genug anliegt, um
definitiv mehrere Messungen zu machen.
MfG, Ozzy
Christoph O. wrote:
> Hi,>> deshalb deaktiviere ich doch die Interrupts,
Das kannst du dir sparen.
Die Interrupts sind während der Ausführung einer ISR
sowieso gesperrt.
> während ich per UART sende.> Erst wenn ich das gemacht habe, setze ich den Timer wieder auf null und> aktiviere die Interrupts.
Das bringt dir aber nichts. Wenn am Int Eingang ein
Puls auftritt dann wird zunächst mal das zugehörige
Flag gesetzt und bei nächster Gelegenheit die zugehörige
ISR aufgerufen. Läuft also während deiner UART
Ausgabe ein Puls am INT0 Eingang ein, so wird die ISR
ausgeführt, sobald das globale Interrupt Flag wieder
freigegeben ist (also beim Beenden der momentan laufenden ISR).
> Wenn ich dann einige Messungen verpasse, ist> das (relativ) egal, da ich weiß, dass das Signal lange genug anliegt, um> definitiv mehrere Messungen zu machen.
Dein Problem ist nicht, dass du eine Messung verpasst.
Dein Problem ist, das während der Ausgabe schon wieder der
nächste Interrupt getriggert wird.
Du müsstest das Interrupt Flag kurz vor Beendigung der ISR
zurücksetzen. Aber auch das hilft dir nichts. Du willst ja nicht
die Zeitspanne von irgendeinem fiktiven Punkt auf der Zeitachse
bis zum nächsten Auftreten des INT0 messen. Du willst die Zeitspanne
zwischen dem Auftreten 2-er INT0 Ereignisse messen.
Egal wie du es drehst und wendest: Die INT0 Behandlung muss so kurz
wie möglich sein. Eine UART Ausgabe passt da einfach nicht in diese
Anforderung.
Hi,
danke für Deine ausführliche Erklärung!!! Die UART-Ausgabe war auch eher
zu Debugging-Zwecken da. Dann werde ich mal mein Programm etwas
umschreiben.
Vielen Dank noch einmal, Ozzy
Du solltest Dein Programm in mehrere parallel arbeitende Tasks
aufteilen. Die ISR des ext.Int (ICP wäre besser geeignet) misst die
Periodendauer (nicht die Frequenz) und legt den Messwert unbehandelt
(also binär) ab. Der UART-Interrupt sendet ein Zeichen aus einem
Ringbuffer und setzt eine Semaphore, wenn der Buffer leer ist. Die
Mainloop wandelt bei leerem Buffer (wenn Semaphore gesetzt ist) den
gerade aktuellen Messwert in ASCII um und legt ihn in den Buffer. Nach
getaner Arbeit geht es in den Sleep-Mode, geweckt wird per ISR. Auf
diese Art hat das Programm noch viele Reserven für andere Tasks. Nur ein
großer Controller und ein schneller Takt alleine garantieren noch lange
kein gutes Projekt...
...
Wenn ich mich nicht verrechnet habe brauchst du für das USART einen
Teiler von 4 um die gewünschte Datenmenge durchzutreten... macht
schlappe 3,6864 MHz für die serielle Schnittstelle!
Bist du dir sicher, dass du nach jedem Intervall das Messergebnis
schicken willst, oder reicht es auch nach jedem 256sten? Macht immer
noch 156 Messungen pro Sekunde und verursacht deutlich weniger Rauschen
der Messwerte.
Musst du die Werte als ASCII schicken? Bin jetzt nicht so fit in C, aber
itoa() erstellt die Dezimalrepräsentation deiner Messung, oder? Wenn du
das auf Hex umstellst sollte man nochmal einiges an Rechenzeit sparen.
Den Versand der Messwerte würde ich interruptgesteuert durchführen, um
die Messungen nicht unnötig zu stören.
Gruß
Kai
ps: bis eben habe ich noch refreshed ;)
Hi,
da ich mir die Daten erst einmal nur zu Testzwecken ausgeben lassen
wollte (später wird das alles mal ganz anders) habe ich mir folgendes
gedacht: Ich mache ein paar Messungen, und ziehe dann den Stecker wieder
von INT0 ab, und erzeuge danach durch eine Verbindung mit 5V einen
Interrupt auf INT1. Soweit mein Code:
1
voidinit_interrupt(void){
2
DDRD=0x00;
3
MCUCR|=(1<<ISC00)|(1<<ISC01);
4
MCUCR|=(1<<ISC10)|(1<<ISC11);
5
EICRA|=(1<<ISC01)|(1<<ISC00);
6
EICRA|=(1<<ISC11)|(1<<ISC10);
7
EIMSK|=(1<<INT0);
8
EIMSK|=(1<<INT1);
9
}
10
11
ISR(INT0_vect){
12
signal=TCNT1;
13
TCNT1=0;
14
}
15
16
ISR(INT1_vect){
17
//char s[7];
18
//uart_sendstring( itoa( start, s, 10 ) );
19
//uart_sendchar( '\n' );
20
uart_sendchar('i');
21
}
Das "uart_sendchar( 'i' );" hatte ich erst einmal nur testweise
reingenommen, aber nun passiert folgendes: selbst wenn ich die Frequenz
an INT0 anlege, werden "i"'s ausgegeben. Also sowohl bei INT0, als auch
bei INT1.
Könnt Ihr mir sagen, wo da mein Fehler liegt?
MfG, Ozzy
Wie hast du den INT1 Eingang beschaltet?
Wenn du den offen gelassen hast, darfst du dich nicht wundern,
dass der Eingang sich so ziemlich jede elektromagnetische
Strahlung in der Umgebung einfängt, die er nur kriegen kann.
Also zumindest einen Widerstand nach Masse darfst du dem
schon spendieren (10k sollten reichen).
Aber warum machst du das eigentlich so kompliziert mit einem
zusätzlichen Interrupt etc, etc.
1
volatileuint16_tsignal;
2
3
....
4
5
ISR(INT0_vect){
6
signal=TCNT1;
7
TCNT1=0;
8
}
9
10
....
11
12
intmain()
13
{
14
uint16_ttmp;
15
chars[7];
16
17
....alleinit
18
19
20
sei();
21
22
while(1){
23
cli();
24
tmp=signal;
25
sei();
26
27
uart_sendstring(itoa(tmp,s,10));
28
uart_sendchar('\n');
29
}
30
}
und schon sendet dir der µC ständig seinen aktuellen
Messwert.
Das
cli();
tmp = signal;
sei();
ist notwendig, damit dir der Interrupt nicht hinterrücks den
Messwert ändern kann, während der itoa ihn in einen ASCII
String verwandelt. Also wird der momentan aktuelle Messwert
in einer Zwischenvariablen gesichert. Und damit während
der Sicherns auch noch sicher gestellt ist, dass signal seinen
Wert nicht ändern kann, werden kurzfristig die Interrupts
verboten.
(Wenn das klappt, dann solltest du dir mal im Datenblatt den
Input Capture Mode des Timers ansehen und überlegen, wie
du denn nutzbringend einsetzen kannst)
Vielleicht habe ich das Problem nicht verstanden, aber ich würde das
Signal an ICP1 anlegen und mir die aktuelle Periodendauer aus dem Wert
im ICR1 abzüglich letzter Wert vom ICR1 errechnen. Wenn die Auflösung
höher werden sollte, würde man dann nicht nur eine Periode messen,
sondern beispielsweise 100, sodaß dann alle 2,5ms ein neuer Meßwert
verfügbar wäre.
Aber vielleicht habe ich das Problem auch nicht verstanden.
Man kann den größten und schnellsten Controller mit einer einzigen
Aufgabe überfordern, wenn diese Aufgabe schlecht programmiert ist. Also
sollte man ein Programm so strukturieren, dass die Aufgaben in
Teilstücke zerlegt werden, die (bei Bedarf) immer nur einen Schritt
ausführen und sofort zur Mainloop zurückkehren, wenn der nächste Schritt
jetzt noch nicht ausgeführt werden kann. Dabei sollten die ISRs so
kurz wie möglich sein, also nur die anfallenden Daten sichern, ggf. eine
Semaphore setzen und dann sofort zur Mainloop zurückkehren. Nur so kann
erreicht werden, dass sich die verschiedenen zu erledigenden Jobs nicht
gegenseitig stören bzw. ausbremsen.
In Deinem Falle böte sich an:
- ICP-ISR (oder ext.-Int) ermittelt Differenz zwischen letztem und
aktuellen Zeitstempel und legt diese in einer globalen Variable ab.
- UDRE-ISR schiebt ein Byte aus einem Array (String) über UART raus.
- Mainloop prüft Array-Zeiger und generiert einen neuen String, wenn das
Array komplett ausgegeben wurde. Während des Kopierens der Daten muss
natürlich die Interruptfreigabe kurzzeitig gesperrt werden, damit das
Kopieren atomar erfolgt, also nicht mittendrin der Wert geändert wird.
...