C Atmega128 Timer(2) Problem

CrazyMetal

Neues Mitglied
19. Dez. 2013
9
0
0
Sprachen
Hallo zusammen,
ich habe einen ATMEGA128 und möchte, dass der Timer2 alle 100 Mikrosekunden überläuft:
Code:
TCCR2 = (1<<CS21);	// Prescale 8
TCNT2 = 56;			// Vorladen mit 56
TIMSK = (1<<TOIE2);	// Overflow-Interrupt für Timer 2 enable

Nun habe ich eine Variable "zaehlRunter_In_us" und diese Variable hat den Wert 7000.
Code:
int zaehlRunter_In_us = 7000;
Die ISR vom Timer2 soll bei jedem überlauf 100 Mikrosekunden davon abziehen.
Code:
ISR(TIMER2_OVF_vect)
{
	// 100us
	TCNT2 = 56;
	
	if(zaehlRunter_In_us > 0)
	{
                LED_TOGGLE();
		zaehlRunter_In_us -= 100;
	}
}
Demnach soll die Variable "zaehlRunter_In_us" nach 7 Millisekunden auf 0 (Null) steht.
Code:
	if(zaehlRunter_In_us == 0)
	{
		// mach was tolles
	}

Leider klappt das nicht so wie ich mir das ausgemalt habe, hat Ihr eine Idee was ich falsch mache?
Ich habe mir in(!) der IF-Abfrage der ISR vom Timer2 eine LED (toggle) anzeigen lassen, doch die geht nicht aus...

Viele Grüße,
Crazy
 
Ich habe mir in(!) der IF-Abfrage der ISR vom Timer2 eine LED (toggle) anzeigen lassen, doch die geht nicht aus...

Erstmal hallo und willkommen :)

Hmmm vielleicht blöde Idee, aber da du dein Code nicht komplett gepostet hast und mich dieser Satz etwas stutzig macht...
Du hast aber schon die Interrupts aktiviert?
Code:
sei();
Ohne dem würde der nie in die ISR springen.

Ist mir schon zig mal passiert ^^

Was ich jetzt auch nicht weiß ist ob der Datentyp int in C (beim AVR) überhaupt negative Zahlen zulässt. Dann würdest du natürlich nie unter 0 kommen. Aber ich kann C höchstens lesen, da kann dir ein Anderer garantiert besser helfen :)
Ich persönlich zähle immer lieber hoch und vergleiche den Zielwert.
 
Ein int beim avrgcc ist ein int16_t, also -32767 bis 32767. :)

Ein uint ist ein uint16_t, also 0 bis 65535.

@TE: Poste mal das gesamte Programm. Eine LED zu toggeln ist in diesem Fall recht sinnfrei, da zu schnell.
 
sei() ist da

Erstmal hallo und willkommen :)
Danke =)

Hmmm vielleicht blöde Idee, aber da du dein Code nicht komplett gepostet hast (...)
Das komplette Programm ist etwas umfangreicher, da es schon ein bestehendes System ist und ich es etwas abändern darf/muss. Mit dem Atmega wird ein Phasenanschnitt (mehrkanalig) gemacht, und ich möchte mit dem Timer in 100 Mirosekunden-Schritten zählen, um die Anschnittzeit und den Zeitraum des Triggersignal für die Antiparallelthyristoren auszuwerten.

Du hast aber schon die Interrupts aktiviert?
Code:
sei();
Ohne dem würde der nie in die ISR springen.
Die Interruputs sind mittels sei(); aktiviert worden, Timer0, Timer1 und Timer3 laufen auch so wie sie sollen.

Was ich jetzt auch nicht weiß ist ob der Datentyp int in C (beim AVR) überhaupt negative Zahlen zulässt. Dann würdest du natürlich nie unter 0 kommen.
Doch, es ist ja kein "unsigned", also kann man in den negativen Bereich gehen.
 
Eine LED zu toggeln ist in diesem Fall recht sinnfrei, da zu schnell.
Nee, eigentlich ist das nicht recht sinnfrei, denn: Klar ich sehe sie leuchten/glimmen, sobald der IF-Block abgearbeitez wird.
Der Wert von dem ich runterzähle wird per Taster gesetzt (momentan zum Test), nach 7 Millisekunden müsste die LED also entweder "normal" leuchten oder aus sein, wenn der Taster nicht gedrückt ist. Damit kann ich schon mal sehen, wie lange die IF-Schleife durchlaufen wird. ;-)
 
Hallo Crazy und Willkommen im Forum!


Du könntest es vielleicht so lösen ...

Dirk :ciao:

Code:
#define COUNTERA_RELOAD    70

volatile uint8_t CounterA = COUNTERA_RELOAD;



ISR(TIMER2_OVF_vect)
{
    // 100us
    TCNT2 = 56;
    
    if (0 == CounterA)
    {

        // 7ms vergangen
        CounterA = COUNTERA_RELOAD;
        [B]// mach was tolles[/B]

    } else {

        CounterA--;
        LED_TOGGLE(); // bei 100us wirst du nichts erkennen, bestenfalls nimmt die Helligkeit etwas ab

    }
    
}
 
Beispielprogramm

Hallo Dirk,
vielen Dank für deinen Vorschlag.

Da keiner von Euch über die Timer-Konfiguration gemeckert hat, gehe ich davon aus, dass ich nicht zu doof bin den Timer einzustellen ;-)
Wie gesagt, ich möchte einen (mehrkanaligen) Phasenanschnitt machen und deshalb in der ISR nur einen Zähler dekrementieren und die
eigentliche Logik im normalem Programm abarbeiten, zumal ich während einem Phasenanschnitt ja auch Wartezeiten habe, die bei mehreren Kanälen eine andere Phase blockieren könnten.
Hier ist einmal ein kleines Beispiel was ich (mit einem Kanal) probiert habe.

Code:
#define ANSCHNITT_ZEIT     70
#define TRIGGER_ZEIT        1

volatile int conterAnschnitt = -1;
volatile int counterTrigger = -1;


ISR(TIMER2_OVF_vect)
{
    // 100us
    TCNT2 = 56;
    
    if(counterAnschnitt > 0)
    {
        counterAnschnitt--;
    }
    
    if(counterTrigger > 0)
    {
        counterTrigger--;
    }
}

void main()
{

    while(1)
    {
            
        if(Nulldurchgang())        // Nulldurchgang erkennen
        {
            counterAnschnitt = ANSCHNITT_ZEIT;        // Wartezeit bis zum Thyristor zünden setzen
            
            //
            // Der auskommentierte Code war zum Testen des Nulldurchgang und funktioniert problemlos.
            //
            //_delay_ms(7);
            //Thyristor_Zuenden();
            //_delay_ms(1);
            //Thyristor_Trigger_Off();
        }
        
        
        if(counterAnschnitt == 0)    // Wartezeit zum Zünden des Thyristor wurde erreicht
        {
            Thyristor_Zuenden();            // Den Thyristor zünden
            counterAnschnitt = -1;            // Zähler auf -1 setzen, damit kein Code-Abschnitt was tut, was er nicht soll
            counterTrigger = TRIGGER_ZEIT;    // Wartezeit zum Ausschalten des Thyristor Trigger-/Zünde-Signal
        }
    
    
        if(counterTrigger == 0)        // Wartezeit zum Ausschalten des Thyristor-Trigger erreicht
        {
            Thyristor_Trigger_Off();        // Thyristor-Trigger-Signal aus
            counterTrigger = -1;            // Zähler auf -1 setzen, damit kein Code-Abschnitt was tut, was er nicht soll
        }
    }
}

Aufgefallen ist mir, dass ich das Schlüsselwort "volatile" vergessen habe O:)
Zu meiner (schwachen) Verteidigung: Selbiges Vorgehen habe ich beim Timer3 verwendet um einen Lüfter für einige Minuten laufen zu lassen, das
funktioniert problemlos.

Kurz noch zur LED: Ich meine die LED leuchtet schwacher als sonst. OK - man kann sie auch an und explizit aus machen, was etwas eindeutiger ist.

-Crazy
 
Hallo Crazy,

"volatile" verhindert, dass der Compiler in main() zu viel optimiert.

Der Compiler hat nicht einen kompletten Überblick über dein Programm. Es kann passieren, dass er in main() komplette Bereiche entfernt (if-Abfrage hat immer das selbe boolsche Ergebnis), da er denkt, dass sich hier eine Variable nie ändert. Er weiß nicht, dass diese Variable ja eventuell in einer ISR geändert wird. Mit "volatile" verhinderst du, dass zu viel optimiert wird.

Noch ein weiteres Problem:
Verwende möglichst nicht zu große Datentypen. Bei dir reicht in der ISR uint8_t. Kritisch ist es, wenn du eine Variable in der ISR und in main() verwendest, welche aus mehreren Bytes besteht. Der Zugriff auf diese Variablen ist ja nicht atomar. Wenn du hier in main() eine Variable veränderst (der Compiler benötigt mehr als eine Instruktion), kann es passieren, dass dir die ISR dazwischen funkt und MSB oder LSB ändert. Du musst bei Mehrbyte-Variablen also selber dafür sorgen, dass die ISR nichts ändern kann. Hier gibt es mehrere Möglichksiten, zum Beispiel bevor du in main änderst, Interrupts global abschalten, dann ändern und dann wieder global aktivieren.

Zu der Timerkonfiguration:
Hier habe ich nur kurz drübergeschaut, scheint zu stimmen. Es ist ja die einfachste Möglichkeit den Timer so laufen zu lassen. Der Nachladewert stimmt, wenn der Systemtakt 16MHz beträgt.
Eleganter ist der CTC Mode des Timers.
Für weitere Erklärungen hierzu kannst du dir auch mal ein kleines Windows-Programm AVR-Timer-Calculator von mir anschauen, den Link im Forum findest du in meinem Blog ...
Meine Softwaretools (XP, Vista, Windows 7)

Noch ein Tipp:
Falls du noch andere Dinge im Hauptprogramm erledigen möchtest ... ich denke die if-Abfragen werden ständig ausgeführt, auch wenn sich die volatile Variablen "nur" alle 100us ändern. Eventuell könntest du hier mehr Bereiche in die ISR verlegen, so dass nicht zu viel unnötige Rechenzeit "verbraucht" wird. Ich muss aber jetzt auch dazu sagen, dass ich dein Programm jetzt nicht detailliert angeschaut habe (ich habe leider wenig Zeit), vielleicht passt es ja auch so, wie es jetzt ist.

Dirk :ciao:
 
"volatile" verhindert, dass der Compiler in main() zu viel optimiert.
Jepp, das haben wir ja mal gelernt – und wie das mit gelerntem so ist :eek:

Der Compiler hat nicht einen kompletten Überblick über dein Programm. Es kann passieren, dass er in main() komplette Bereiche entfernt (if-Abfrage hat immer das selbe boolsche Ergebnis), da er denkt, dass sich hier eine Variable nie ändert. Er weiß nicht, dass diese Variable ja eventuell in einer ISR geändert wird. Mit "volatile" verhinderst du, dass zu viel optimiert wird.
Wobei ich nicht glaube das die IF-Blöcke entfernt werden, weil der Startwert -1 ist und die Vergleichsvarable in dem IF-Block geändert wird. Aber du hast recht, Vorsicht ist besser als Nachsicht.

Verwende möglichst nicht zu große Datentypen. Bei dir reicht in der ISR uint8_t. Kritisch ist es, wenn du eine Variable in der ISR und in main() verwendest, welche aus mehreren Bytes besteht. Der Zugriff auf diese Variablen ist ja nicht atomar. Wenn du hier in main() eine Variable veränderst (der Compiler benötigt mehr als eine Instruktion), kann es passieren, dass dir die ISR dazwischen funkt und MSB oder LSB ändert. Du musst bei Mehrbyte-Variablen also selber dafür sorgen, dass die ISR nichts ändern kann. Hier gibt es mehrere Möglichksiten, zum Beispiel bevor du in main änderst, Interrupts global abschalten, dann ändern und dann wieder global aktivieren.
Ok, nur wenn ich uint-Datentypen verwende, kann ich nicht „-1“ zuweisen, damit die IF-Blöcke (x==0) nicht im nächsten Zyklus sofort wieder durchlaufen werden, hier müsste ich zusätzlich uint-Variablen nehmen um ein Flag zu setzen (x == 0 && flag == 1). Guck dir am besten mal den geänderten Code an.

Der Nachladewert stimmt, wenn der Systemtakt 16MHz beträgt.
Eleganter ist der CTC Mode des Timers.
Ok, den Timer kann ich auch im CTC-Modus betreiben, wobei das momentan ja keinen Einfluss auf die Konfiguration haben sollte/dürfte.

Für weitere Erklärungen hierzu kannst du dir auch mal ein kleines Windows-Programm AVR-Timer-Calculator von mir anschauen, den Link im Forum findest du in meinem Blog ...
Meine Softwaretools (XP, Vista, Windows 7)
Danke für den Link zu deinem Blog, da sind ja sehr nützliche Tools :)
Den AVR-Timer-Calculator habe ich vor 2 Tagen schon gefunden und geladen, nachdem ich merkte, dass irgendwas nicht passt.

Falls du noch andere Dinge im Hauptprogramm erledigen möchtest ... ich denke die if-Abfragen werden ständig ausgeführt, auch wenn sich die volatile Variablen "nur" alle 100us ändern. Eventuell könntest du hier mehr Bereiche in die ISR verlegen, so dass nicht zu viel unnötige Rechenzeit "verbraucht" wird.
Ähm, nee. Schau dir das Programm noch mal in Ruhe an, ich denke nicht das man mit der ISR viel machen kann (bei der Aufgabe).


Code:
#define ANSCHNITT_ZEIT     70
#define TRIGGER_ZEIT        1

volatile uint8_t conterAnschnitt = 0;
volatile uint8_t counterTrigger = 0;

volatile uint8_t counterAnschnittGesperrt = 1;
volatile uint8_t counterTriggerGesperrt = 1;


ISR(TIMER2_OVF_vect)
{
    // 100us
    TCNT2 = 56;
    
    if(counterAnschnitt > 0 && counterAnschnittGesperrt == 0)
    {
        counterAnschnitt--;
    }
    
    if(ccounterTrigger > 0 && counterTriggerGesperrt == 0)
    {
        counterTrigger--;
    }
}

void main()
{

    while(1)
    {
            
        if(Nulldurchgang())        // Nulldurchgang erkennen
        {
            counterAnschnitt = ANSCHNITT_ZEIT;  // Wartezeit bis zum Thyristor zünden setzen
            counterAnschnittGesperrt = 0;		// Sperr-Flag aufheben
        }
        
        
        if(counterAnschnitt == 0 && counterAnschnittGesperrt == 0)    // Wartezeit zum Zünden des Thyristor wurde erreicht
        {
            Thyristor_Zuenden();            // Den Thyristor zünden
            counterAnschnittGesperrt = 1;   // Sperr-Flag setzen
            counterTrigger = TRIGGER_ZEIT; 	// Wartezeit zum Ausschalten des Thyristor Trigger-/Zünde-Signal
			counterTriggerGesperrt = 0;		// Sperr-Flag aufheben
        }
    
    
        if(counterTrigger == 0 && counterTriggerGesperrt == 0)        // Wartezeit zum Ausschalten des Thyristor-Trigger erreicht
        {
            Thyristor_Trigger_Off();        	// Thyristor-Trigger-Signal aus
            counterTriggerGesperrt = 1;         // Sperr-Flag setzen
        }
    }
}

-Crazy
 
Hallo Crazy,

ja bei deinem Programm wurde nichts wegoptimiert, sonst würde es nicht funktionieren.

Extra Bytes für die Sperrung zu verwenden, ist doch in Ordnung. Vielleicht kannst du auch int8_t verwenden, da passt die 70 auch rein. Egal, wenns funktioniert lass es so :)

Ja ich sehe im Hauptprogramm pollst du auf Nulldurchgang. Wenn du die anderen Programmteile (if-Abfragen) in die ISR übernimmst, hast du eine Verzögerung. Man kann dies also nicht einfach so machen.

Eventuell könntest du aber auch einen Ext Interrupt oder einen CompareInterrupt verwenden, um den Nulldurchgang (kommt ja ledigleich alle 20ms vor?!) zu erkennen und in dieser ISR sofort drauf reagieren. Dadurch hättest du in main() nichts mehr. Es ist aber auch alles egal, wenn du sonst keine größeren Programmteile mehr ausführst.



Dirk :ciao:
 
Hi Dirk,
ein externer Interrupt für den Nulldurchgang, wäre mir zwar sehr lieb, jedoch ist die Platine fertig und die Software soll neu – der Nulldurchgang wird aber nicht an einem Interrupt-Pin abgefragt, von daher fällt die Option leider weg.

Der Nulldurchgang kommt alle 10ms vor, es gibt ja auch den Wechsel von positiver und negativer Halbwelle und ich möchte beide anschneiden. Was mich stutzig macht ist, dass die Verwendung von delay_ms() zu den gewünschten Anschnitt führt, jedoch meine Zähler-Logik eine Nulllinie auf das Oszilloskop zaubert :-(

Das mit „volatile“ werde ich ausprobieren, wobei ich das auch bei der Lüfer-Variable vergessen habe und es dort geht (Timer im 100ms Takt und entsprechend wird runtergezählt bis Null, danach kommt die -1 ins Spiel). Ansonsten macht das Programm nix besonderes, außer das L1 und L2 abgefragt werden. Der Wald und die vielen Bäume....

-Crazy
 
Hi Crazy,
Was mich stutzig macht ist, dass die Verwendung von delay_ms() zu den gewünschten Anschnitt führt, jedoch meine Zähler-Logik eine Nulllinie auf das Oszilloskop zaubert :-(

die Triggerzeit ist ja sehr kurz, max 100us, da die Variable den Wert eins hat, wenn getriggert werden soll. Da der Timer bzw. das 100us Event aber quasi asynchron zum Hauptprogramm läuft, kann es passieren, dass wenn du die Variable auf 1 setzt, die TimerISR diese sofort auf 0 setzt. Zumindest hast du hier keine definierten 100us. Das würde ich eventuell mal einfach als _delay_us(100) realisieren, zumindest zum Testen.

Ansonsten bau testweise einen Zähler in die ISR ein und lasse eine LED blinken (zB 500ms Periodendauer), um zu testen ob der Timer richtig läuft.

Dirk :ciao:

Code:
ISR()
{
  // 100us
   static uint16_t counter = 5000;

   counter--;
   if (0 == counter)
   {
      // 500ms
      counter = 5000;
      TOGGLE_LED
   }

}
 
Hi Dirk,
das mit dem Counter in der ISR ist eine gute Idee, das werde ich entsprechend testen.
Ich hatte mich im Beispiel vertippt, meine ISR wird alle 100us aufgerufen, der Trigger hat einen Wert von 10(!) also 1ms, die Anschnittverzögerung einen Wert von 70 also 7ms.

Von der Theorie her sollte das Programm also so laufen wie ich mir das denke, respektive wie es im Nulldurchgang-Polling mit der delay_ms() aus tut, oder? :confused:

-Crazy
 
Hi Crazy,
Von der Theorie her sollte das Programm also so laufen wie ich mir das denke, respektive wie es im Nulldurchgang-Polling mit der delay_ms() aus tut, oder? :confused:

hmmm, ich denke es sollte so funktionieren.

füge bei den if-Abragen überall einmal Klammern bei den jeweiligen Ausdrücken hinzu. Der Compiler müsste hier eigentlich warnen ...

Code:
if ([B]([/B]counterAnschnitt > 0[B])[/B] && [B]([/B]counterAnschnittGesperrt == 0[B])[/B])

Dirk :ciao:
 
Hi Dirk,
erst einmal vielen Dank für deine Mühe und natürlich auch an alle Thread-Beteiligten. Ich werde die hier vorgeschlagenen Ansätze in das Programm aufnehmen und probieren. Jedoch nicht mehr in diesem Jahr, jetzt ist Urlaub. :cool:

Ich wünsche Dir schöne Weihnachten und einen Guten Rutsch (falls man sich vorher nicht mehr ließt).

-Crazy
 
Mööp!

Hi Dirk,
das ganze hat mir keine Ruhe gelassen, also musste ich es heute morgen mit mäßigem Erfolg testen.

Ich tippe grade auf den Timer, ich habe mir in der ISR eine LED anzeigen lassen, wenn der Anschnittzähler grade nicht in Verwendung ist - und siehe da: das ist nie der Fall. Andersrum zählt das Biest ewig runter sofern ich die LED beim Dekrementieren aktiviere.

Meine Theorie: Der Timer läuft zu langsam und wird bevor er Null erreicht wieder auf den Startwert gesetzt, somit würde der else-Block nie durchlaufen.

Code:
ISR(TIMER2_OVF_vect)
{
    // 100us
    TCNT2 = 56;
    
    if((counterAnschnitt > 0) && (counterAnschnittGesperrt == 0))
    {
        counterAnschnitt--;
		LED_5_Aus();
    }
	else
	{
		LED_5_Toggle();
	}
    
    if((counterTrigger > 0) && (counterTriggerGesperrt == 0))
    {
        counterTrigger--;
    }
}

EDIT:
Halt Stopp!
Ich habe grade deinen Code mit der LED in die ISR gepackt, das die LED tut wie ihr geheißen, ergo der Timer ist OK.

Code:
   static uint16_t counter = 5000;

   counter--;
   if (0 == counter)
   {
	   // 500ms
	   counter = 5000;
	   LED_5_Toggle();
   }
 
Hi Crazy,

wenn du nicht weiter kommst, stell doch den letzten Stand deines Programms noch einmal in das Forum.

Ich habe zwar im Moment nicht so viel Zeit, aber ich würde bei der nächsten Gelegenheit noch mal drüberschauen ... vielleicht hat inzwischen auch jemand anderes eine Idee :)

Dirk :ciao:
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)