mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik PWM Signal auf Dauersignal umwandeln


Autor: Christian W. (christian_w)
Datum:

Bewertung
0 lesenswert
nicht 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


  // Hauptschleife
  while(1)
  {
    
    // WK-IN vorhanden?
    if ( (PIND & (1<<PIND2)) )
    {
      // Signal ausgeben
      PORTD &= ~(1<<PD4);
    }

    // WK-IN nicht aktiv?
    if ( !(PIND & (1<<PIND2)) )
    {
      // Auf 100% Tastverhältnis "erweitern" und dann WK-OUT deaktivieren
      _delay_ms(100);  
      PORTD |= (1<<PD4);
      
    }


  }

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
voilatile uint8_t timeout;

TIMER_ISR () // 1ms
{
   if (timeout) timeout--;
}

main () 
{

  // Hauptschleife
  while(1)
  {
   
    // WK-IN 
    if ( PIND & (1<<PIND2) ) timeout= 100; // Signal da --> timeout auf 100ms setzen

    // Ausgang
    if (timeout) PORTD |=  (1<<PD4);  // setzen, solange timeout noch > 0
    else         PORTD &= ~(1<<PD4);
      
  }
}

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...

Autor: TT. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
volatile uint8_t timeout;

TIMER_ISR () // 1ms
{
   if (timeout) timeout--;
}

EXTERNERPIN_ISR () // Pinchange oder allg. Flanken-Interrupt für PIND2
{
    timeout= 100; // Signal da --> timeout auf 100ms setzen
}

main () 
{
  ISR_INIT(); // deine Arbeit
  // Hauptschleife
  while(1)
  {
    // Ausgang
    if (timeout) PORTD |=  (1<<PD4);  // setzen, solange timeout noch > 0
    else         PORTD &= ~(1<<PD4);     
  }
}

Autor: Christian W. (christian_w)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Guru (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Christian W. (christian_w)
Datum:

Bewertung
0 lesenswert
nicht 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
#include <avr/io.h> // Grundfunktionen
#define F_CPU 1000000UL // CPU Frequenz
#include <util/delay.h> // Warteschleifen
#include <avr/interrupt.h> // Interrupts



// Variable für Timer
volatile uint8_t timeout;



ISR (TIMER0_OVF_vect) // Interruptauslösung bei Timerüberlauf 1 ms
{
  if (timeout)
  {
    // Zähler um 1 verringern
    timeout--;
  }
}



ISR (INT0_vect) // Auf wechselnde Flanke an INT0 (PD2) reagieren
{
  // Falls Signalflanke erkannt, dann Timeout wieder auf 100 hochsetzen
  timeout=100;
}



int main(void)
{


// Eingänge
// PD2 als WK-IN


// Ausgänge
DDRB = 0b00000001;
DDRC = 0b00100000;
DDRD = 0b11110000; 
// PB0 als WK-OUT-LED -
// PC5 als Betriebs-LED
// PD4 als Signal WK-OUT (Aus, wenn High)
// PD5 als WK-IN-LED +
// PD6 als WK-IN-LED -
// PD7 als WK-OUT-LED +


// Betriebs-LED einschalten
PORTC |= (1<<PC5);


// WK-OUT deaktivieren (Aus, wenn High)
PORTD |= (1<<PD4);


// Auf wechselnde Flanke an INT0 reagieren
MCUCR &= ~(1<<ISC01);
MCUCR |= (1<<ISC00);


// Externen Interrupt an INT0 aktivieren
GICR |= (1<<INT0);


// Timer aktivieren, CLK/1024, 1 ms
TCCR0 |= (1<<CS00);
TCCR0 &= ~ (1<CS01);
TCCR0 |= (1<<CS01);


// Timer 0 Overflow Interrupt Enable
TIMSK |= (1<<TOIE0);


// Interrupts erlauben
sei();


  // Hauptschleife
  while(1)
  {

    // Ausgang aktivieren, solange timeout nicht Null
    if (timeout) PORTD &= ~(1<<PD4); // Low=Ein
    else         PORTD |= (1<<PD4);  // High=Aus

  }

  return 0;

}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.