Forum: Mikrocontroller und Digitale Elektronik PWM Singal Messung mit ATMega328P


von l. w. (Gast)


Lesenswert?

Hallo zusammen,

hatte schon mal einen Post erstellt bei dem ich dasselbe Problem 
geschildert hab. Habe es aber noch nicht hinbekommen das Ganze zu lösen. 
Also es geht darum die High-Pulslänge eines PWM Signals zu messen oder 
die Duty Cycle das ist eig. egal. Hab da mal wieder im Internet etwas 
recherchiert und etwas gefunden. Das funktioniert allerdings nur mit dem 
ATmega16 bzw. 32. Ich würde das allerdings gerne mit dem Atmega328P 
realisieren. Jetzt bräuchte ich Hilfe wie man den Code verändern muss um 
das Ganze auf dem Atmega328P zum laufen zu bringen. Und welcher Pin dann 
der "Input Capture Pin" an dem ATmega328P ist, also der Pin an dem ich 
das PWM Signal anlegen muss. Am Ende sollen dann aufgrund der Duty Cycle 
LEDs angesteuert werden.

Hier der Link zum 
Originalartikel:https://www.electronicwings.com/avr-atmega/atmega1632-timer-input-capture-mode

Und hier meine Wandlung für den ATmega328P, die allerdings nicht 
funktioniert ... (Hab alles was mit dem Display zu tun hat entfernt, das 
brauche ich nicht und habe das Register TIFR in TIFR0 umbenannt da es 
das beim ATmega328 nicht gibt)

Wäre sehr sehr sehr dankbar, wenn mir da jemand weiterhelfen kann...
Vielen Dank schon mal!!
1
/*
2
  Measuring ATmega16 frequency and duty cycle using input capture
3
  http://www.electronicwings.com
4
 */
5
6
7
#define F_CPU 1000000UL
8
#include <avr/io.h>
9
#include <util/delay.h>
10
#include <avr/interrupt.h>
11
#include <stdlib.h>
12
13
#define LED1 (1<<PD5)
14
15
16
int main ( )
17
{
18
  unsigned int a,b,c,high,period;
19
  
20
  PORTD = 0xFF;      /* Turn ON pull-up resistor */
21
  
22
  while(1)
23
  {
24
    TCCR1A = 0;
25
    TCNT1=0;
26
    TIFR0 = (1<<ICF1);    /* Clear ICF (Input Capture flag) flag */
27
28
    TCCR1B = 0x41;    /* Rising edge, no prescaler */
29
    while ((TIFR0&(1<<ICF1)) == 0);
30
    a = ICR1;      /* Take value of capture register */
31
    TIFR0 = (1<<ICF1);    /* Clear ICF flag */
32
    
33
    TCCR1B = 0x01;    /* Falling edge, no prescaler */
34
    while ((TIFR0&(1<<ICF1)) == 0);
35
    b = ICR1;      /* Take value of capture register */
36
    TIFR0 = (1<<ICF1);    /* Clear ICF flag */
37
    
38
    TCCR1B = 0x41;    /* Rising edge, no prescaler */
39
    while ((TIFR0&(1<<ICF1)) == 0);
40
    c = ICR1;      /* Take value of capture register */
41
    TIFR0 = (1<<ICF1);    /* Clear ICF flag */
42
43
    TCCR1B = 0;      /* Stop the timer */
44
    
45
    if(a<b && b<c)    /* Check for valid condition, 
46
          to avoid timer overflow reading */
47
    
48
    #define LED1 (1<<PD5)
49
    {
50
      high=b-a;
51
      period=c-a;
52
53
54
      /* Calculate duty cycle */
55
            float duty_cycle =((float) high /(float)period)*100;
56
      
57
      if(duty_cycle >= 1)
58
      {
59
        DDRD |= LED1;
60
        PORTD ^= LED1;
61
        _delay_ms(100);
62
      }  
63
      
64
    }
65
    
66
  }
67
68
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

l. w. schrieb:
> Und hier meine Wandlung für den ATmega328P, die allerdings nicht
> funktioniert ...
Was funktioniert denn nicht?
Was erwartest du?
Und was passiert stattdessen?

> TCCR1B = 0;      /* Stop the timer */
Funktionierende Programme gehen so: ein Timer wird 1x gestartet und dann 
nur noch abgefragt und die Differenz zum letzen ausgelesenen Timerwert 
ermittelt.

l. w. schrieb:
> Also es geht darum die High-Pulslänge eines PWM Signals zu messen oder
> die Duty Cycle das ist eig. egal. Hab da mal wieder im Internet etwas
> recherchiert und etwas gefunden.
Aber eben nicht verstanden. Wie wärs mit selber mal drüber Nachdenken? 
Ich verwende Code aus dem Internt nur, wenn ich ihn (wenigstens 
grundlegend) verstanden habe. Sonst suche ich hinterher noch die Fehler, 
die Andere gemacht haben.

Was ist übrigens aus meinem Vorschlag im 
Beitrag "Servo-Pulssignalläge messen mit Atmega328p" geworden?

l. w. schrieb:
> Kannst du mir bei der lesetimeraus() Funktion noch weiterhelfen.
Die sieht etwa so aus:
lesetimeraus( void) {  return TCNT1; }

von S. Landolt (Gast)


Lesenswert?

> habe das Register TIFR in TIFR0 umbenannt
Mit TIFR1 versuchen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

l. w. schrieb:
>
1
> float duty_cycle =((float) high /(float)period)*100;
2
> 
3
>       if(duty_cycle >= 1)
4
>       {
5
>         DDRD |= LED1;
6
>         PORTD ^= LED1;
7
>         _delay_ms(100);
8
>       }
9
>

Das kann man um einiges optimieren.
1
        float dutycycle = ...
Muss das wirklich float sein? Das kostet richtig CPU-Zeit.
Vielleicht reicht ja:
1
        int duty_cycle = (100 * high) / period;
Beachte hier die Klammern: Erst Multiplikation, um große Zahl zu 
erhalten, um sie dann durch die kleinere zu teilen. Damit rettest Du 
immerhin zwei Dezimalstellen bei der Division, was im folgenden 
vollkommen ausreicht, auch wenn Du später mit anderen Prozentzahlen als 
nur 1 Prozent vergleichen möchtest, z.B. 25 für 25%, 50 für 50%, 75 für 
75%.

Die Zeile:
1
       if(duty_cycle >= 1)
soll abfragen, ob der duty cycle größer gleich 1 Prozent ist? Wenn das 
so gewollt ist, kann das so bleiben.

Die Zeile:
1
          DDRD |= LED1;
kann komplett vor die while-Schleife gezogen werden. Es bringt überhaupt 
nichts, dieses Bit immer wieder neu zu setzen.

Sonst kann ich mich nur Lothar anschließen: Was funktioniert denn nicht? 
Was erwartest Du, was erhältst Du?

: Bearbeitet durch Moderator
von l. w. (Gast)


Lesenswert?

Lothar M. schrieb:
> Was funktioniert denn nicht?
> Was erwartest du?
> Und was passiert stattdessen?

Nochmal zurück zu deinem Code Lothar. Sorry, dass ich nerve aber wär mir 
echt wichtig das hinzubekommen...
Wie initialisiert man dann hier den Timer und würde das mit der Funktion 
lesetimeraus so schon passen oder muss man da noch was ergänzen.
1
#define F_CPU 1000000UL
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
#include <stdlib.h>
6
7
#define LED1 (1<<PD5)
8
9
lesetimeraus(void)
10
{
11
  return TCNT1;
12
}
13
14
int main ( )
15
{
16
  // Timer initialisieren
17
18
  DDRD |= LED1;
19
  
20
  int rcpinold, rcpin;
21
  unsigned long risetime, pulsdauer;
22
  
23
  while(1)
24
  {
25
    rcpin = (PIND & (1<<PD2));
26
    if(rcpin!=rcpinold)
27
    {
28
      risetime = lesetimeraus();
29
    }
30
    else
31
    {
32
      pulsdauer = lesetimeraus() - risetime;
33
    }  
34
    rcpinold = rcpin;
35
    
36
    if(pulsdauer >=1)
37
    {
38
      PORTD ^= LED1;
39
      _delay_ms(500);
40
    }
41
  }
42
43
}

Ich würde gerne ein PWM-Signal an den PIN PD2 anlegen. Aus diesem 
angelegten Signal soll der Mikrocontroller die HIGH-Pulslänge ermitteln. 
Bei verschiedenen HIGH Pulslängen sollen LEDs mit verschiedenen 
Funktionen angesteuert werden. Also z. B. bei einer High-Pulslänge von 
1ms bis 1,5ms soll beispielsweise eine Doppelblitz ausgegeben werden und 
von 1,5ms bis 2ms ein Dreifachblitz.

Frank M. schrieb:
> Die Zeile:       if(duty_cycle >= 1)
> soll abfragen, ob der duty cycle größer gleich 1 Prozent ist? Wenn das
> so gewollt ist, kann das so bleiben.

Damit wollte ich nur ausprobieren, ob das funktioniert ...

von c-hater (Gast)


Lesenswert?

l. w. schrieb:

> Ich würde gerne ein PWM-Signal an den PIN PD2 anlegen.

Tja, µC-Programmierung ist kein Wunschkonzert. Wenn du ein optimales 
Ergebnis erwartest, dann musst du das Signal auch an den/die am besten 
dafür geeigneten Pins anlegen, nicht an irgendeinen.

Da der Mega328P nur über einen Timer mit InputCapture-Funktion verfügt 
und nur über eine Möglichkeit, das entsprechenden Signal an irgendeinen 
Pin zu legen, wirst du eben diesen Pin benutzen müssen.

Und das ist nicht PD2, sondern PB0. So steht's im Datenblatt. Und es 
führt überhaupt kein Weg darum herum. Jedenfalls keiner, der die 
Eigenschaften der Zeitmessung nicht verschlechtern würde, weil du dann 
halt nicht die dafür optimale Peripherie verwenden kannst.

Lies' das verschissene Datenblatt und lerne es zu verstehen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

du musst dich entscheiden. Soll es mittels Timer 1 Input Capture präzise 
Takt genau gemessen werden, dann bist du wie c-hater schreibt auf Pin 
PB0 (ICP1) festgenagelt. Muss es nicht 100% Takt genau sein, kannst du 
auf die beiden Pin Interrupts INT0/1 ausweichen. Oder du nimmst die Pin 
Change Interrupts. Laut deiner letzten Beschreibung muss das wohl nicht 
so genau sein. Deswegen kannste locker die PCINTs verwenden und hast 
freie Pinwahl.

von m.n. (Gast)


Lesenswert?

Veit D. schrieb:
> Muss es nicht 100% Takt genau sein, kannst du
> auf die beiden Pin Interrupts INT0/1 ausweichen. Oder du nimmst die Pin
> Change Interrupts. Laut deiner letzten Beschreibung muss das wohl nicht
> so genau sein. Deswegen kannste locker die PCINTs verwenden und hast
> freie Pinwahl.

Wenn es hilft, hier ein Beispiel mit INT0/INT1: 
Beitrag "Stoppuhr – Geschwindigkeit – Pulsweite mit Atmega88"
Die Auflösung im µs-Bereich sollte doch reichen.

von Peter D. (peda)


Lesenswert?

c-hater schrieb:
> Und das ist nicht PD2, sondern PB0. So steht's im Datenblatt. Und es
> führt überhaupt kein Weg darum herum.

Naja, man kann die Capture-Funktion auch auf den Analog-Comparator 
routen und diesen wiederum auf einen Eingang des ADC.
Je nach Bauform ergibt das 8 bzw. 10 mögliche Pins als Capture-Eingang:
PB0, PD7, PC5..PC0, ADC7, ADC6

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das mit dem Analog-Comparator ist auch eine interessante Idee.
Und plötzlich hat der TO die Qual der Wahl.  :-)

von c-hater (Gast)


Lesenswert?

Peter D. schrieb:

> Naja, man kann die Capture-Funktion auch auf den Analog-Comparator
> routen und diesen wiederum auf einen Eingang des ADC.
> Je nach Bauform ergibt das 8 bzw. 10 mögliche Pins als Capture-Eingang:
> PB0, PD7, PC5..PC0, ADC7, ADC6

Stimmt, das wäre möglich. Aber PD2 ist immer noch nicht dabei...

von Joachim B. (jar)


Lesenswert?

c-hater schrieb:
> Lies' das verschissene Datenblatt

war die korrekte Übersetzung von RTFM nicht mal?
"read the famos manual"

von c-hater (Gast)


Lesenswert?

Joachim B. schrieb:

> war die korrekte Übersetzung von RTFM nicht mal?
> "read the famos manual"

Was ist "famos"? Vermutlich gemeint war "famous". Aber auch das kann ich 
nirgendwo finden im Zshg. mit "RTFM". Nein, das F steht schon für 
"fucking" und "verschissen" ist durchaus schon ein deutlich entschärfte 
Übersetzung.

Das müssen auch die zarten Seelen aushalten können!

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.