Forum: Mikrocontroller und Digitale Elektronik PWM-Start nutzen


von alguesto (Gast)


Lesenswert?

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.

von MAx_123 (Gast)


Lesenswert?

Erzeuge den Interrupt bei Überlauf und invertiere den Ausgang.

von Düsendieb (Gast)


Lesenswert?

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() ;

von alguesto (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von alguesto (Gast)


Lesenswert?

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;

von Karl H. (kbuchegg)


Lesenswert?

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!

von alguesto (Gast)


Lesenswert?

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
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

1. Wie oft in der Sekunde wird die ISR angesprungen?
2. Wie lang dauert der uart_puts()-Aufruf innerhalb der ISR?

von alguesto (Gast)


Lesenswert?

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?)

von Karl H. (kbuchegg)


Lesenswert?

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)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von alguesto (Gast)


Lesenswert?

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?

von alguesto (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.