Hallo, ich erzeuge über den Timer0 eine FastPWM. Nun möchte ich während des High-Pegels ein anderes Signal mit dem ADC messen. Wie bekomme ich nun den steigende Flanke des PWM-Ausgangs erkannt? Gibt es da ein Flag welches der Timer setzen kann? Es ist bestimmt ganz einfach, aber irgendwie... Danke schonmal.
z.B. für den Timer 1 TCCR1A = ((1 <<WGM11)|(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<COM1B0)) ; // Mode 14 + inverting mode TCCR1B =((1 <<WGM13)|(1 <<WGM12)|(1<<CS10)) ; // Mode 14 + No prescaling TIMSK1= (1 <<TOIE1); // Die ISR (TIMER1_OVF_vect) beim Erreichen von ICR1 aufrufen TCNT1 = 0 ; // reset timer / counterregister sei() ;
Hallo, erstmal danke für die Antworten und entschuldige, dass ich so lang für eine Antwort gebraucht habe. So ganz durchsteige ich die Funktion nur nicht. ICR1 muss ich doch noch einen Wert zuweisen, oder? (Nur welchen) Genauso verhält es sich für OCR1A/OCR1B? Mir fehlt die Verbindung zu meiner eigentlichen PWM des Timers0.
Die Verbindung besteht darin, dass du ja WEISST wann die steigende Flanke im PWM Signal kommt! Nämlich dann, wenn der Timer im FastPWM Modus (und entsprechender Pin Einstellung) bei 0 zu zählen anfängt. Und das geschieht dadurch, dass der Timer einen Overflow macht.
Also ich habe jetzt mal den geposteten code out-of-the-box so genommen.
1 | TCCR1A = ((1 <<WGM11)|(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<COM1B0)); // Mode 14 + inverting mode |
2 | TCCR1B =((1 <<WGM13)|(1 <<WGM12)|(1<<CS10)) ; // Mode 14 + No prescaling |
3 | TIMSK1= (1 <<TOIE1); // Die ISR (TIMER1_OVF_vect) beim Erreichen von ICR1 aufrufen |
4 | TCNT1 = 0 ; // reset timer / counterregister |
Damit habe ich also den Timer1 eingestellt In der Interruptroutine führe ich nun eine Funktion aus. Zur Kontrolle lasse ich mir die ermittelten Werte auf den Terminal schicken. So nun kommt allerdings nichts. Ich schließe daraus, dass die Interruptroutine nicht angesprungen wird. (Funktion und Senden per UART funktioniert, ist getestet).
1 | ISR (TIMER1_OVF_vect) |
2 | {
|
3 | cli(); |
4 | char buffer[20]; |
5 | itoa(getAD(2,3), buffer, 10); |
6 | uart_puts(buffer); |
7 | sei(); |
8 | }
|
Zur Vervollständigung Timer0:
1 | TCCR0A|=(1<<WGM01)|(1<<WGM00); //PWM-Mode 3 (FastPWM) |
2 | TCCR0A|=(1<<COM0B1)|(1<<COM0A1); //Non-Inverting PWM |
3 | TCCR0B|=(1<<CS01); //Prescaler auf n=8 , f(PWM)=F_CPU/n*256 |
4 | TCNT0 = 0xFF; |
5 | OCR0A=30; |
6 | OCR0B=30; |
alguesto schrieb: > So nun kommt allerdings nichts. Ich schließe daraus, dass die > Interruptroutine nicht angesprungen wird. > (Funktion und Senden per UART funktioniert, ist getestet). Daraus schliesse ich wiederrum, dass du keinen sei() im Programm hast. Zeig vollständigen Code. Schön langsam sind es die meisten Regulars hier im Forum leid, sich immer wieder aus Codeausschnitten (in denen der Fehler nicht steckt), den Rest zusammenzureimen und zu Raten, welche Fehler du noch hättest machen können. > ISR (TIMER1_OVF_vect) > { > cli(); braucht keiner > char buffer[20]; > itoa(getAD(2,3), buffer, 10); > uart_puts(buffer); > sei(); den willst du hier ganz sicher nicht haben!
Karl Heinz Buchegger schrieb: > Daraus schliesse ich wiederrum, dass du keinen sei() im Programm hast. Doch den habe ich in der main(). Hier nochmal alles. Habe die sei() und cli() in der ISR rausgenommen.
1 | #include <avr/io.h> |
2 | #include <stdio.h> |
3 | #include <stdint.h> |
4 | #include <avr/interrupt.h> |
5 | #include "uart.h" |
6 | #include <stdlib.h> |
7 | #include <avr/pgmspace.h> |
8 | #include <util/delay.h> |
9 | |
10 | #ifndef TRUE
|
11 | #define TRUE 1
|
12 | #define FALSE 0
|
13 | #endif
|
14 | |
15 | #ifndef F_CPU
|
16 | #define F_CPU 8000000UL
|
17 | #endif
|
18 | |
19 | #define UART_BAUD_RATE 9600
|
20 | #define length 10
|
21 | |
22 | uint8_t dimlvl[10]={0x00, 0x1A, 0x33, 0x4D, 0x66, 0x80, 0x99, 0xB3, 0xCC, 0xE9, 0xFF}; |
23 | |
24 | |
25 | |
26 | uint16_t getAD(uint8_t channel, uint8_t n) |
27 | {
|
28 | ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1); |
29 | ADMUX=channel; |
30 | ADMUX|=(1<<REFS0); |
31 | uint16_t result=0; |
32 | for(uint8_t i=0;i<n;i++) |
33 | { ADCSRA|=(1<<ADSC); |
34 | while(ADCSRA&(1<<ADSC)); |
35 | result+=ADCW; |
36 | }
|
37 | result/=n; |
38 | return result; |
39 | }
|
40 | |
41 | |
42 | int main(void) |
43 | {
|
44 | uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); |
45 | sei(); |
46 | DDRD|=0b01100000; //Pin5&6 PortD als Ausgang |
47 | TCCR0A|=(1<<WGM01)|(1<<WGM00); //PWM-Mode 3 (FastPWM) |
48 | TCCR0A|=(1<<COM0B1)|(1<<COM0A1); //Non-Inverting PWM |
49 | TCCR0B|=(1<<CS01); //Prescaler auf n=8 , f(PWM)=F_CPU/n*256 |
50 | OCR0A=30; |
51 | OCR0B=30; //Timerschwelle festlegen |
52 | TCNT0 = 0xFF; |
53 | TCCR1A = ((1 <<WGM11)|(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<COM1B0)); // Mode 14 + inverting mode |
54 | TCCR1B =((1 <<WGM13)|(1 <<WGM12)|(1<<CS10)) ; // Mode 14 + No prescaling |
55 | TIMSK1= (1 <<TOIE1); // Die ISR (TIMER1_OVF_vect) beim Erreichen von ICR1 aufrufen |
56 | TCNT1 = 0 ; // reset timer / counterregister |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | while(1) |
63 | {
|
64 | |
65 | }
|
66 | return 0; |
67 | }
|
68 | |
69 | ISR (TIMER1_OVF_vect) |
70 | {
|
71 | |
72 | char buffer[20]; |
73 | itoa(getAD(2,3), buffer, 10); |
74 | uart_puts(buffer); |
75 | |
76 | }
|
1. Wie oft in der Sekunde wird die ISR angesprungen? 2. Wie lang dauert der uart_puts()-Aufruf innerhalb der ISR?
Bei einer PWM-Freuquenz von f=8MHz/(8*256)=3,9kHz ist das eine sehr beträchtliche Anzahl. Deine Vermutung ist also, dass die Abarbeitung gar nicht so schnell erfolgen kann und während der Abarbeitung schon wieder ein neuer Interrupt aufgetreten ist. Deshalb hatte ich die Interrupts innerhalb der Routine abgeschaltet, was mir ja wieder um die Ohren geworfen wurde ;) Oder ich sollte die uart_puts nicht in der ISR abhandeln sondern ausserhalb?(Müßte ich dafür dann die Funktion als volatile deklarieren?)
alguesto schrieb: > Deine Vermutung ist also, dass die Abarbeitung gar nicht so schnell > erfolgen kann und während der Abarbeitung schon wieder ein neuer > Interrupt aufgetreten ist. > Deshalb hatte ich die Interrupts innerhalb der Routine abgeschaltet, was > mir ja wieder um die Ohren geworfen wurde ;) > Oder ich sollte die uart_puts nicht in der ISR abhandeln sondern > ausserhalb?(Müßte ich dafür dann die Funktion als volatile deklarieren?) Machs halt erst mal einfach: Keine ADC, keine UART. Wenn die ISR aufgerufen wird, wird eine LED eingeschaltet. Das erzählt dir erst mal alles, was du wissen willst. Je mehr du machst und je komplexer dein Testprogramm ist, desto mehr Fehlermöglichkeiten gibt es auch. (Und ich bin ehrlich gesagt auch nicht sicher, ob im Modus 14 vom Timer 1 überhaupt ein Overflow ausgelöst wird. Lässt sich aber schnell mit einer LED abklären)
alguesto schrieb: > Bei einer PWM-Freuquenz von f=8MHz/(8*256)=3,9kHz ist das eine sehr > beträchtliche Anzahl. Jedes Zeichen dauert bei 9600Bd knapp 1 Millisekunde. Du kannst also gar nicht bei jedem Overflow die ADC-Werte über UART senden, sondern vielleicht bei jedem zehnten - wenns hochkommt. Solche zeitintensiven Sachen gehören einfach nicht in die ISR. Dort kannst Du höchstens ein Flag setzen, dass mittlerweile ein ADC-Wert ermittelt wurde. Das eigentliche Senden über UART erfolgt dann in der main-funktion - oder auch Interrupt-gesteuert, indem man ein FIFO füllt. Abgesehen davon habe ich dieselben Zweifel wie Karl Heinz, dass hier überhaupt ein OVF-Interrupt ausgelöst wird. Daher ist ein Test mit einer LED sehr sinnvoll. > Deshalb hatte ich die Interrupts innerhalb der Routine abgeschaltet, was > mir ja wieder um die Ohren geworfen wurde ;) Beim Betreten einer ISR werden die Interrupts sowieso global abgeschaltet, beim Beenden der ISR werden sie wieder eingeschaltet. Deine cli-/sei-Kombination in der ISR war daher hyperfluid. Aber was passiert: Kommt während des Abarbeitens einer ISR ein neuer Interrupt, merkt sich der µC das in einem Flag und führt die entsprechende ISR direkt nach Abarbeiten der vorhergehenden ISR aus! Im schlechtesten Falle macht der µC also nichts mehr anderes als ISRs auszuführen - in Deinem Fall immer dieselbe. Das wird aber alles hier im AVR-Tutorial beschrieben. Du solltest Dir solche Grundlagen erst einmal aneignen, bevor Du komplizierte Sachen anpackst. > Oder ich sollte die uart_puts nicht in der ISR abhandeln sondern > ausserhalb?(Müßte ich dafür dann die Funktion als volatile deklarieren?) Ja. Nein.
Der uart_puts war ja eigentlich auch nur zum schauen ob es funktioniert, werd den dann mal in die main schreiben. Das mit der "LED-Methode" werde ich mal austesten, werd mir einen Ausgang am Oszi anschauen. Bin aber heute nicht mehr am Objekt,kann es also erst später einmal testen. Eure Befürchtungen beunruhigen mich allerdings etwas, dass es so prinzipiell nicht funktionieren kann. Gibt es denn noch Alternativen?
So war heute mal am Objekt. Also die ISR wird tatsächlich angesprungen. Ich habe einen Pin in der ISR gesetzt und ausserhalb wieder zurückgestzt. Problem ist jetzt noch, dass es frequenzmässig noch nicht zusammen passt, was ich allerdings auf den (nicht eingestellten) Prescaler des Timers1 zurückführe. Des weiteren ist das Duty-cycle zum Timer0 invertiert, ist ja auch so eingestellt, aber wird dadurch die ISR nicht gerade zum ungewollten Zeitpunkt angesprungen?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.