Forum: Mikrocontroller und Digitale Elektronik PWM Signal auf Dauersignal umwandeln


von Christian W. (christian_w)


Lesenswert?

Hallo zusammen,

ich bin auf der Suche nach einem C-Code für den Atmega8.

An PD2 liegt ein Rechtecksignal an mit ca. 10 Hz. Wird dieses Signal 
erkannt, soll an PD4 ein Dauersignal ausgegeben werden, solange das 
Rechtecksignal an PD2 anliegt.

Habe zwar schon einen Code gebastelt aber der ist mir zu ungenau und 
reagiert natürlich etwas "träge". Ausgang PD4 reagiert manchmal zu lange 
nicht, und zwar genau dann, wenn an PD2 gerade eine steigende Flanke 
kommt, wenn die Schleife beim "delay" Befehl ist, so ist das Signal an 
PD2 dann wieder futsch, wenn die Schleife von vorne beginnt und PD4 ist 
ungewollt nicht aktiv.

Jetzt möchte ich gerne das ganze über Interrupt Programmierung lösen. 
Macht das Sinn? Oder kann man den bestehenden Code so hinbiegen, dass es 
passt?

Danke

Christian

1
  // Hauptschleife
2
  while(1)
3
  {
4
    
5
    // WK-IN vorhanden?
6
    if ( (PIND & (1<<PIND2)) )
7
    {
8
      // Signal ausgeben
9
      PORTD &= ~(1<<PD4);
10
    }
11
12
    // WK-IN nicht aktiv?
13
    if ( !(PIND & (1<<PIND2)) )
14
    {
15
      // Auf 100% Tastverhältnis "erweitern" und dann WK-OUT deaktivieren
16
      _delay_ms(100);  
17
      PORTD |= (1<<PD4);
18
      
19
    }
20
21
22
  }

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


Lesenswert?

Christian W. schrieb:
> Habe zwar schon einen Code gebastelt aber der ist mir zu ungenau und
> reagiert natürlich etwas "träge".
Schneller als 100ms wird nicht mal der beste Code reagieren können...

Ich würde das etwa so machen:
1
voilatile uint8_t timeout;
2
3
TIMER_ISR () // 1ms
4
{
5
   if (timeout) timeout--;
6
}
7
8
main () 
9
{
10
11
  // Hauptschleife
12
  while(1)
13
  {
14
   
15
    // WK-IN 
16
    if ( PIND & (1<<PIND2) ) timeout= 100; // Signal da --> timeout auf 100ms setzen
17
18
    // Ausgang
19
    if (timeout) PORTD |=  (1<<PD4);  // setzen, solange timeout noch > 0
20
    else         PORTD &= ~(1<<PD4);
21
      
22
  }
23
}

Abhaängig vom möglichen Tastverhältnis der PWM kann das timeout auch auf 
kleinere Werte gesetzt werden. Wenn z.B. die High-Zeit nicht kleiner als 
30ms werden kann, dann reicht ein Timeout von ca. 70ms aus, weil ja 
solange das Eingangssignal 1 ist, der timeout immer wieder auf den 
Startwert (hier 100) gesetzt wird...

von TT. (Gast)


Lesenswert?

Versuch mal ein ein Delay von 50ms (oder besser 51ms wenn eine 
Verzögerung von 1ms drin is;)). (10Hz --> Periode 100ms = 50ms high; 
50ms low)
Zur Flankenerkennung brauchst Du bei 10Hz wohl nicht unbedingt einen 
Interrupteingang. Eine saubere Lösung wäre allerding mit einem 
Timerinterrupt nach den 50ms, da kein busy-wait...

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


Lesenswert?

TT. schrieb:
> Versuch mal ein ein Delay von 50ms (oder besser 51ms wenn eine
> Verzögerung von 1ms drin is;)
Blöd nur, wenn die PWM zufällig mal 30/70 oder gar 99/1 beträgt...  :-o

von Karl H. (kbuchegg)


Lesenswert?

TT. schrieb:

> Zur Flankenerkennung brauchst Du bei 10Hz wohl nicht unbedingt einen
> Interrupteingang.

Ich würds trotzdem machen. PWM Pulse können sehr kurz sein.
Den Rest so wie Lothar das gezeigt hat.

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


Lesenswert?

Lothar Miller schrieb:
> voilatile
Da musste ich doch gerade mal über mich selber lachen... :D

Karl Heinz Buchegger schrieb:
> Den Rest so wie Lothar das gezeigt hat.
Mit dem Interrupt-Pin wird das Ganze noch hübscher:
1
volatile uint8_t timeout;
2
3
TIMER_ISR () // 1ms
4
{
5
   if (timeout) timeout--;
6
}
7
8
EXTERNERPIN_ISR () // Pinchange oder allg. Flanken-Interrupt für PIND2
9
{
10
    timeout= 100; // Signal da --> timeout auf 100ms setzen
11
}
12
13
main () 
14
{
15
  ISR_INIT(); // deine Arbeit
16
  // Hauptschleife
17
  while(1)
18
  {
19
    // Ausgang
20
    if (timeout) PORTD |=  (1<<PD4);  // setzen, solange timeout noch > 0
21
    else         PORTD &= ~(1<<PD4);     
22
  }
23
}

von Christian W. (christian_w)


Lesenswert?

Hallo,

danke für die Antworten.

Habe ich hier noch ein Verständnisproblem?

Die Atmega8-Doku sagt folgendes zu den Externen Interrupts:

"Note that recognition of falling or rising edge interrupts on INT0 and 
INT1 requires the presence of an I/O clock, described in “Clock Systems 
and their Distribution” on page 23"

Heißt jetzt "requires the presence of an I/O clock", dass ich da noch 
sowas wie einen Taktgeber programmieren/verwenden muss?

Einen internen Takt von 1 MHz habe ich ja sowieso...

Ich versteh die Aussage momentan nicht so richtig ;-) ;-)

Christian

von spess53 (Gast)


Lesenswert?

Hi

>Heißt jetzt "requires the presence of an I/O clock", dass ich da noch
>sowas wie einen Taktgeber programmieren/verwenden muss?

Nein. Mit dem IO-Clock (im allg. die Taktfrequenz des AVR) werden alle 
getakteten IO-Module (z.B Timer und auch der Interrupteingang) 
automatisch versorgt.

Siehe Datenblatt: 'Clock Systems and their Distribution'.

MfG Spess

von Guru (Gast)


Lesenswert?

>Heißt jetzt "requires the presence of an I/O clock", dass ich da noch
>sowas wie einen Taktgeber programmieren/verwenden muss?

Nein. "I/O clock" ist ein Terminus aus dem Datenblatt der einen 
bestimmten Takt meint der intern aus dem Haupttakt abgeleitet wird. 
Sinngemäß bezeichnet er eine bestimmte Taktdomäne.

Der Satz ist im Zusammenhang mit den Power-Down-, resp. Sleep-Modi 
wichtig, weil dann bestimmte Taktsignale, je nach Modus, nicht mehr 
erzeugt werden. Es geht hier also darum, das Flankeninterrupts nur bei 
vorhandenem I/O-Takt, also bei bestimmten Sleep Modi funktionieren.

Die Ursache hat mit dem Mechanismus für die Erkennung von Flanken zu 
tun. Dieser benötigt nämlich einen Takt.

Näheres im Datenblatt.

von Christian W. (christian_w)


Lesenswert?

Hallo zusammen,

danke für die Antworten.

Habe jetzt anhand der Vorschläge einen C-Code erstellt, der soweit gut 
klappt, aber:

PD4 ist etwas mehr als 1 Sekunde aktiv, sollte aber nur ca. 100 ms aktiv 
sein.

Was ist da noch falsch? Ich komm nicht drauf...

Danke!

Christian
1
#include <avr/io.h> // Grundfunktionen
2
#define F_CPU 1000000UL // CPU Frequenz
3
#include <util/delay.h> // Warteschleifen
4
#include <avr/interrupt.h> // Interrupts
5
6
7
8
// Variable für Timer
9
volatile uint8_t timeout;
10
11
12
13
ISR (TIMER0_OVF_vect) // Interruptauslösung bei Timerüberlauf 1 ms
14
{
15
  if (timeout)
16
  {
17
    // Zähler um 1 verringern
18
    timeout--;
19
  }
20
}
21
22
23
24
ISR (INT0_vect) // Auf wechselnde Flanke an INT0 (PD2) reagieren
25
{
26
  // Falls Signalflanke erkannt, dann Timeout wieder auf 100 hochsetzen
27
  timeout=100;
28
}
29
30
31
32
int main(void)
33
{
34
35
36
// Eingänge
37
// PD2 als WK-IN
38
39
40
// Ausgänge
41
DDRB = 0b00000001;
42
DDRC = 0b00100000;
43
DDRD = 0b11110000; 
44
// PB0 als WK-OUT-LED -
45
// PC5 als Betriebs-LED
46
// PD4 als Signal WK-OUT (Aus, wenn High)
47
// PD5 als WK-IN-LED +
48
// PD6 als WK-IN-LED -
49
// PD7 als WK-OUT-LED +
50
51
52
// Betriebs-LED einschalten
53
PORTC |= (1<<PC5);
54
55
56
// WK-OUT deaktivieren (Aus, wenn High)
57
PORTD |= (1<<PD4);
58
59
60
// Auf wechselnde Flanke an INT0 reagieren
61
MCUCR &= ~(1<<ISC01);
62
MCUCR |= (1<<ISC00);
63
64
65
// Externen Interrupt an INT0 aktivieren
66
GICR |= (1<<INT0);
67
68
69
// Timer aktivieren, CLK/1024, 1 ms
70
TCCR0 |= (1<<CS00);
71
TCCR0 &= ~ (1<CS01);
72
TCCR0 |= (1<<CS01);
73
74
75
// Timer 0 Overflow Interrupt Enable
76
TIMSK |= (1<<TOIE0);
77
78
79
// Interrupts erlauben
80
sei();
81
82
83
  // Hauptschleife
84
  while(1)
85
  {
86
87
    // Ausgang aktivieren, solange timeout nicht Null
88
    if (timeout) PORTD &= ~(1<<PD4); // Low=Ein
89
    else         PORTD |= (1<<PD4);  // High=Aus
90
91
  }
92
93
  return 0;
94
95
}

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.