www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Probleme mit Timer bei Phasenanschnitt


Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen,
ich habe mir einen Dimmer mit einem Atmega48 aufgebaut. Der µC bekommt 
über einen Optokoppler jeden Nulldurchgang der Netzspannung auf den 
externen Interrupteingang 0. Diese erkennt er schonmal. Wenn ich jetzt 
ein Programm bastel, was einfach über _delay_ms nach dem Nulldurchgang 
ein bisschen warten und dann den Triac für 200us auf High legt, 
funktionert das auch wunderbar.
Da ich dem µC die Zeit, die er warten soll, via Rs232 mitteilen möchte, 
würde ich die ganze Geschichte über einen Timer realisieren.
Ich weiß auch, dass dieses Thema schon x-mal durchdiskutiert wurde, aber 
irgendwie werde ich aus den anderen Beiträgen auch nicht schlau.:(
#define F_CPU 4000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

 
int zeit;

int main(void)
{
DDRC = 0xff;

EICRA |= (0<<ISC01 | 0<<ISC00);
EIMSK |= (1<<INT0);

sei();
zeit = 150;
  while(1)
  {

  }
}

ISR( INT0_vect ) {
  TCCR0A = (1<<WGM01); // CTC Modus
    TCCR0B |= (1<<CS00); // Prescaler 8
    // ((1000000/8)/1000) = 125
    OCR0A = zeit;
 
  //Compare Interrupt erlauben
    TIMSK0 |= (1<<OCIE0A);


}

ISR( TIMER0_COMPA_vect ) {
TCNT0 = 0;
PORTC |= (1 << PC5);
_delay_us(100);
PORTC &= ~(1 << PC5);
}

Problem ist jetzt nur, das nicht erst gewartet wird, sondern der Impuls 
direkt am Nulldurchgang kommt (nach meinen Beobachtungen am Oszi).
Der interne Takt beträgt 4Mhz.
Ich hoffe ihr könnt und wollt mir helfen.

Gruß Christianlienen

Autor: Philipp Kälin (philippk) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum machst du das in der ISR?
TCCR0A = (1<<WGM01); // CTC Modus
TCCR0B |= (1<<CS00); // Prescaler 8
// ((1000000/8)/1000) = 125
OCR0A = zeit;
nimm das mal ins main, du musst den Timer nicht immer neu 
initialisieren. Beim CTC wird das TCNT0 Register selbständig 
zurückgesetzt, du musst das nicht selber machen.

Der effektive Fehler liegt aber wahrscheinlich hier:
TCCR0B |= (1<<CS01); // Prescaler 8
// ((1000000/8)/1000) = 125
wenn du einen Prescaler von 8 willst (wie es im Kommentar steht), dann 
solltest du CS01 auf 1 setzen, nicht CS00. Du hast momentan keinen 
Prescaler.

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Philipp Kälin schrieb:
> Warum machst du das in der ISR?TCCR0A = (1<<WGM01); // CTC Modus
> TCCR0B |= (1<<CS00); // Prescaler 8
> // ((1000000/8)/1000) = 125
> OCR0A = zeit;
> nimm das mal ins main, du musst den Timer nicht immer neu
> initialisieren. Beim CTC wird das TCNT0 Register selbständig
> zurückgesetzt, du musst das nicht selber machen.

woher weiß der Timer denn dann, dass er in dem Interrupt starten soll?

Philipp Kälin schrieb:
> Der effektive Fehler liegt aber wahrscheinlich hier:TCCR0B |= (1<<CS01); // 
Prescaler 8
> // ((1000000/8)/1000) = 125
> wenn du einen Prescaler von 8 willst (wie es im Kommentar steht), dann
> solltest du CS01 auf 1 setzen, nicht CS00. Du hast momentan keinen
> Prescaler.

Entschuldigung, der Kommentar stand da noch von anderen Versuchen.

Gruß
Christian

Autor: David Madl (md2k7)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian L. schrieb:
> woher weiß der Timer denn dann, dass er in dem Interrupt starten soll?

Dort musst du TCNT0 auf 0 setzen (in INT0, nicht im Timer-Compare!).

Mit einem Prescaler von 1 kommst du übrigens nie im Leben auf 10 ms. Da 
brauchst du bei einem 8-bit Timer und 4 MHz schon mindestens einen 
Prescaler von 256.

Gruß
David

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#define F_CPU 4000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

 
int zeit;

int main(void)
{
DDRC = 0xff;
zeit = 79;
EICRA |= (0<<ISC01 | 0<<ISC00);
EIMSK |= (1<<INT0);
TCCR0A = (1<<WGM01); // CTC Modus
TCCR0B |= (1<<CS02); // Prescaler 256

TIMSK0 |= (1<<OCIE0A);
OCR0A = zeit;

sei();

  while(1)
  {

  }
}


ISR( INT0_vect ) {
TCNT0 = 0;
}

ISR( TIMER0_COMPA_vect ) {
PORTC |= (1 << PC5);
_delay_us(100);
PORTC &= ~(1 << PC5);
}


Soweit ich alles richtig verstanden habe, sollte das Programm ja nun so 
aussehen. Ich habe Prescaler auf 256 geändert. So müsste der Timer nun, 
um die Phase ca. in der Mitte abzuschneiden, bis 79 zählen.
Leider kommt nun gar kein Zündimpuls mehr.
Gruß
Christian

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mit
EICRA |= (0<<ISC01 | 0<<ISC00);
hast du einen lowlevel int, du musst nur mit einer Flanke triggern!
Ausserdem bekommst du nur bei den positiven oder negativen Halbwelle 
einen  brauchbaren Impuls von deinem Optokoppler, deshalb sollte der 
Timer so laufen, das er die andere Halbwelle überbrücken kann.
Das Optimum währe ein Überlauf bei 255 alle 10ms, dann kannst du den 
Timer gleich als FastPWM laufen lassen. Da währe aber ein Quarz mit 
6,5563MHz notwendig.

Sascha

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> hast du einen lowlevel int, du musst nur mit einer Flanke triggern!
> Ausserdem bekommst du nur bei den positiven oder negativen Halbwelle
> einen  brauchbaren Impuls von deinem Optokoppler, deshalb sollte der
> Timer so laufen, das er die andere Halbwelle überbrücken kann.

Nein das stimmt so nicht. Ich bekomme in jeder Halbwelle einen Impuls, 
da ich das Signal mit einem Brückengleichrichter gleichgerichtet habe, 
bevor es in Richtung Optokoppler geht.

Das mit dem Lowlevel int könnte jedoch sein, da werde ich morgen direkt 
mal ins datenblatt schauen.

Vielen Dank für eure Unterstützung bis jetzt!

Gruß Christian

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Abend,
nachdem ich die anderen Probleme alle hoffentlich aus dem Weg geräumt 
habe, steht bereits das nächste an. Mein Programm sieht immernoch so 
aus, wie das, was oben zu sehen ist. Ich habe es jedoch so geändert, 
dass das externe Interrupt nun bei einer steigenden Flanke starten 
SOLLTE. Leider tut es das nicht, sodass nun der Timer jetzt immer vor 
sich herläuft, und den Triac in falschen Zeitabständen zündet.
Kann es sein, das der Impuls für den µC zu kurz ist, um den Interrupt zu 
starten?
Mein Atmega48 läuft im Moment mit 4Mhz.
Mein Eingangssignal sieht wie das folgende aus:
Beitrag "Phasenanschnitt Dimmer mit AVR will nicht :- ("

Könnte es evtl. helfen, wenn ich ein Quarz mit einer hohen Frequenz 
benutzen würde, z.B. 16Mhz?

Gruß
Christian

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ist das Sync in deiner Schaltung auch so beschaltet wie in dem 
verlinkten Thread?
evl. ist dein OK zu langsam - du könntest mal den Pullup verkleinern, 
bzw. auf jeden Fall wie im Beispiel einen externen benutzen.

Sascha

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Den Pullup habe ich von 4k7 auf 1k geändert. Leider startet der µC den 
Interrupt immernoch nicht. Er erkennt einfach keinen High Level, obwohl 
das Signal dort einwandfrei anliegt.
Grüße
Christian

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wie lang ist denn die H- bzw. L-Zeit deines Syncsignals?

die Initialisierung sieht jetzt so aus ?!
EICRA |= (1<<ISC01) | (1<<ISC00);

Sascha

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für eure Unterstützung:)
Mittlerweile läuft der Timer mit den Interrupts auch so, wie ich es 
gerne hätte.
Nun habe ich aber leider ein anderes Problem:
Ich möchte jetzt die Zeit, die gewartet werden soll, über den UART 
empfangen. Leider bekomme ich das noch nicht ganz so hin:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!
#define BAUD 9600L          
 
// Berechnungen
// clever runden
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  
// Reale Baudrate
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     
// Fehler in Promille 
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) 

 
// globale Variablen für den UART
 
// Puffergrösse in Bytes, RX und TX sind gleich gross
#define uart_buffer_size 3             

uint8_t zeit;

void uart_init(void)
{
    UCSR0B |= (1<<RXEN0);                         // UART RX einschalten
    UCSR0C |= (1<<USBS0)|(1<<UCSZ00);  // Asynchron 8N1 
 
    UBRR0H = UBRR_VAL >> 8;
    UBRR0L = UBRR_VAL & 0xFF;
}

uint8_t uart_getc(void)
{
    while (!(UCSR0A & (1<<RXC0)))   // warten bis Zeichen verfuegbar
        ;
    return UDR0;                   // Zeichen aus UDR an Aufrufer zurueckgeben
}
 
void uart_gets( char* Buffer, uint8_t MaxLen )
{
  uint8_t NextChar;
  uint8_t StringLen = 0;
 
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen
 
                                  // Sammle solange Zeichen, bis:
                                  // * entweder das String Ende Zeichen kam
                                  // * oder das aufnehmende Array voll ist
  while( NextChar != '\n' && StringLen < MaxLen - 1 ) {
    *Buffer++ = NextChar;
    StringLen++;
    NextChar = uart_getc();
  }
 
                                  // Noch ein '\0' anhängen um einen Standard
                                  // C-String daraus zu machen
}


int main(void)
{
volatile buffer;
DDRC = 0xff;
zeit = 120;

uart_init();
EIMSK |= (1<<INT0);
EICRA |= (1<<ISC01 | 1<<ISC00);//Externen Interrupt erlauben

TCCR0A = (1<<WGM01); // CTC Modus
TCCR0B |= (1<<CS02)|(1<<CS00); // Prescaler 256
TIMSK0 |= (1<<OCIE0A);



sei();

  while(1)
{
uart_gets(zeit,3);
OCR0A = zeit;
}
}



ISR(INT0_vect) {

TCNT0 = 0;

}

ISR(TIMER0_COMPA_vect) {
PORTC |= (1 << PC5);
_delay_us(200);
PORTC &= ~(1 << PC5);
}

Wenn ich das Programm nun ausführe, ist die Glühbirne zunächst aus. 
Sobald ich jetzt irgendeinen Dezimalwert über das Terminal sende, 
schaltet die Lampe ein. Leider lässt sich die Lampe nicht regeln und 
somit die Helligkeit auch nicht verändern. Ausschalten lässt sich die 
Glühbirne übrigens auch nicht.

Bitte helft mir!

Gruß
Christian

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uart_gets gibt doch einen String zurück wie soll da ein brauchbarer Wert 
für OCR0A draus werden? Da musst du den String schon erst mal wieder in 
einen Bytewert umrechnen.

Sascha

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja das hört sich ganz logisch an. Ich weiß jedoch nicht, wie ich das 
machen soll. Hast du vielleicht einen Tipp für mich?
Gruß
Christian

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jetzt hört's auf - also mit C kenne ich mich nicht aus!

Schau dich mal um - da wird sich sicher was finden lassen. Oder übergib 
zu Testzwecken nur ein Byte, das dann direkt als Wert interpretiert 
wird.

Sascha

Autor: Christian L. (christianlienen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Trotzdem Vielen Dank Sasha!
Ich werde mich dann mal umsehen und gegebenenfalls einen neuen Thread 
posten.

Grüße
Christian

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.