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
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.
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
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
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
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
> 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
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
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
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
wie lang ist denn die H- bzw. L-Zeit deines Syncsignals?
die Initialisierung sieht jetzt so aus ?! 1 | EICRA |= (1<<ISC01) | (1<<ISC00);
|
Sascha
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
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
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
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
Trotzdem Vielen Dank Sasha!
Ich werde mich dann mal umsehen und gegebenenfalls einen neuen Thread
posten.
Grüße
Christian
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|