Probleme mit Timer bei Phasenanschnitt

OP #1853195
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.:(
1
#define F_CPU 4000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5

6
 
7
int zeit;
8

9
int main(void)
10
{
11
DDRC = 0xff;
12

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

16
sei();
17
zeit = 150;
18
  while(1)
19
  {
20

21
  }
22
}
23

24
ISR( INT0_vect ) {
25
  TCCR0A = (1<<WGM01); // CTC Modus
26
    TCCR0B |= (1<<CS00); // Prescaler 8
27
    // ((1000000/8)/1000) = 125
28
    OCR0A = zeit;
29
 
30
  //Compare Interrupt erlauben
31
    TIMSK0 |= (1<<OCIE0A);
32

33

34
}
35

36
ISR( TIMER0_COMPA_vect ) {
37
TCNT0 = 0;
38
PORTC |= (1 << PC5);
39
_delay_us(100);
40
PORTC &= ~(1 << PC5);
41
}

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
Persönliche Seite #1853212
Lesenswert?

Warum machst du das in der ISR?
1
TCCR0A = (1<<WGM01); // CTC Modus
2
TCCR0B |= (1<<CS00); // Prescaler 8
3
// ((1000000/8)/1000) = 125
4
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:
1
TCCR0B |= (1<<CS01); // Prescaler 8
2
// ((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.
OP #1853230
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
OP #1853275
Lesenswert?

1
#define F_CPU 4000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5

6
 
7
int zeit;
8

9
int main(void)
10
{
11
DDRC = 0xff;
12
zeit = 79;
13
EICRA |= (0<<ISC01 | 0<<ISC00);
14
EIMSK |= (1<<INT0);
15
TCCR0A = (1<<WGM01); // CTC Modus
16
TCCR0B |= (1<<CS02); // Prescaler 256
17

18
TIMSK0 |= (1<<OCIE0A);
19
OCR0A = zeit;
20

21
sei();
22

23
  while(1)
24
  {
25

26
  }
27
}
28

29

30
ISR( INT0_vect ) {
31
TCNT0 = 0;
32
}
33

34
ISR( TIMER0_COMPA_vect ) {
35
PORTC |= (1 << PC5);
36
_delay_us(100);
37
PORTC &= ~(1 << PC5);
38
}

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
#1853739
Lesenswert?

mit
1
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
OP #1853826
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
OP #1856290
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
OP #1863960
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:
1
#define F_CPU 16000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5

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

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

23
uint8_t zeit;
24

25
void uart_init(void)
26
{
27
    UCSR0B |= (1<<RXEN0);                         // UART RX einschalten
28
    UCSR0C |= (1<<USBS0)|(1<<UCSZ00);  // Asynchron 8N1 
29
 
30
    UBRR0H = UBRR_VAL >> 8;
31
    UBRR0L = UBRR_VAL & 0xFF;
32
}
33

34
uint8_t uart_getc(void)
35
{
36
    while (!(UCSR0A & (1<<RXC0)))   // warten bis Zeichen verfuegbar
37
        ;
38
    return UDR0;                   // Zeichen aus UDR an Aufrufer zurueckgeben
39
}
40
 
41
void uart_gets( char* Buffer, uint8_t MaxLen )
42
{
43
  uint8_t NextChar;
44
  uint8_t StringLen = 0;
45
 
46
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen
47
 
48
                                  // Sammle solange Zeichen, bis:
49
                                  // * entweder das String Ende Zeichen kam
50
                                  // * oder das aufnehmende Array voll ist
51
  while( NextChar != '\n' && StringLen < MaxLen - 1 ) {
52
    *Buffer++ = NextChar;
53
    StringLen++;
54
    NextChar = uart_getc();
55
  }
56
 
57
                                  // Noch ein '\0' anhängen um einen Standard
58
                                  // C-String daraus zu machen
59
}
60

61

62
int main(void)
63
{
64
volatile buffer;
65
DDRC = 0xff;
66
zeit = 120;
67

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

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

76

77

78
sei();
79

80
  while(1)
81
{
82
uart_gets(zeit,3);
83
OCR0A = zeit;
84
}
85
}
86

87

88

89
ISR(INT0_vect) {
90

91
TCNT0 = 0;
92

93
}
94

95
ISR(TIMER0_COMPA_vect) {
96
PORTC |= (1 << PC5);
97
_delay_us(200);
98
PORTC &= ~(1 << PC5);
99
}

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren