Forum: Compiler & IDEs Sicherer Programmaufbau für wiederkehrende Aufgaben


von Martin (Gast)


Lesenswert?

Ich möchte einem Programm schreiben, das alle x Sekunden einen Pin 
rücksetzt, dieser Pin kann mit Tastendruck wieder auf 1 gesetzt werden:

Bis jetzt habe ich es wie folgt versucht (mit Codevision):
1
unsigned int i=0; 
2
// Timer 0 overflow interrupt service routine
3
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
4
{
5
// Place your code here
6
    i=i+1; 
7
    if(i == 6250)  
8
    {   
9
    i=0;
10
    PORTA.1=0; 
11
    }
12
}
13
14
void main(void)
15
{
16
..Deklarationsteil
17
18
while (1) {
19
if(TASTER)
20
{
21
    delay_us(300);     
22
         #asm("CLI")    
23
24
    PORTA.1=1;     
25
    i=0;   
26
         #asm("SEI")    
27
}
28
}

Das funktioniert ja soweit (prinzipiell). Doch eigentlich dachte ich 
PORTA.1 müsste nachdem die Taste gedrückt wurde, immer x Sekunden lang 1 
sein um dann auf 0 zu wechseln.
Wenn ich den Taster kurz drücke (aber länger als 300us) geht der Pin 
gleich nach dem Loslassen wieder auf 0 zurück. Warum?

von Klaus W. (mfgkw)


Lesenswert?

Irgendwie hast du etwas vom Programm unterschlagen.

Vielleicht willst du bei einer fallenden Flanke den IR auslösen, tust es 
aber bei einer steigenden?

von Martin (Gast)


Lesenswert?

Ja, die Initialisierung des Timers:
[c]

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: 8bit top=FFh
TCCR0A=0x00;
TCCR0B=0x02;
TCNT0H=0x00;
TCNT0L=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x02;

#asm("sei")
[c/]

von ge-nka (Gast)


Lesenswert?

// Clock value: 1000,000 kHz

1000000/256=3906 mal pro sekunde wird dein timer0_ovf_isr aufgerufen,

    i=i+1;
    if(i == 6250)

6250/3906 = 1,6 sekunden sollte PORTA.1=1 sein.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Da i sowohl in main als auch der ISR genutzt wird, solltest Du Dir mal 
ansehen, was das Schlüsselwort volatile für eine Bedeutung hat.

von Martin (Gast)


Lesenswert?

Vielen Dank für den "volatile-Tipp"!

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
+

Ich wenn mit dem Ozsi nachmesse, komm ich darauf, dass meine 
Interrupt-Routine alle 2ms aufgerufen wird.

Nur wie geht das rechnerisch?

1/(1 000 000 000Hz)*256=0.256us

Wie komme ich auf den Unterschied?

von Andreas W. (geier99)


Lesenswert?

Martin schrieb:

> Wie komme ich auf den Unterschied?

hast du einen externen Quarz dran, oder verwendest du den internen 
RC-Oszillator?

von Martin (Gast)


Lesenswert?

Ich verwende den internen Oszillatior, wobei

Das Fusebit CKDIV8 einen Hacken hat und SUT_CKSEL auf 
"INTRCOSC_8MHZ_6CK_14CK_64MS" steht.

Zudem habe ich im Code den Clock-Teiler eingestellt:
// Crystal Oscillator division factor: 8
#pragma optsize-
CLKPR=0x80;
CLKPR=0x03;
#ifdef OPTIMIZE_SIZE
#pragma optsize+
#endif

Ich habe gemessen wenn ich einen Ausgang auf 1 schalte und wieder 
zurück, so dauert es 2us, d.h. für jeden Schaltvorgang 1us = 1MHz.
Das wäre ja in Ordnung.

von Karl H. (kbuchegg)


Lesenswert?

Martin schrieb:
> Vielen Dank für den "volatile-Tipp"!
>
> //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
>
> Ich wenn mit dem Ozsi nachmesse, komm ich darauf, dass meine
> Interrupt-Routine alle 2ms aufgerufen wird.
>
> Nur wie geht das rechnerisch?
>
> 1/(1 000 000 000Hz)*256=0.256us

Ich denke nicht, dass dein µC mit 1Ghz getaktet wird.

von Karl H. (kbuchegg)


Lesenswert?

Das ...
1
// Timer/Counter 0 initialization
2
// Clock source: System Clock
3
// Clock value: 1000,000 kHz
4
// Mode: 8bit top=FFh
5
TCCR0A=0x00;
6
TCCR0B=0x02;
7
TCNT0H=0x00;
8
TCNT0L=0x00;
9
OCR0A=0x00;
10
OCR0B=0x00;
11
// Timer(s)/Counter(s) Interrupt(s) initialization
12
TIMSK=0x02;
... ist genau das, was ich im Codevision Wizard hasse. Um rauszukriegen, 
was da tatsächlich eingestellt wird, muss man sich erst mal exzessiv 
durchs Datenblatt wühlen um zu entschlüsseln, welche Bits da eigentlich 
in den einzelnen Registern gesetzt werden und um dann weiter zu 
ergründen, was sie bedeuten.
Eines ist ziemlich sicher: das hier
TCCR0B=0x02;
ist kein Vorteiler von 1. Ich bin allerdings auch zu faul rauszusuchen, 
was das jetzt tatsächlich für ein Vorteiler ist.

von Martin (Gast)


Lesenswert?

Entschuldige, ich habe noch einen wichtigen Teil vergessen hier 
anzugeben.

// Crystal Oscillator division factor: 8
#pragma optsize-
CLKPR=0x80;
CLKPR=0x03;
#ifdef OPTIMIZE_SIZE
#pragma optsize+
#endif


TIMSK=0x02;
Heißt nur, dass der Timer Interrupt aktiviert wird.

von Karl H. (kbuchegg)


Lesenswert?

Martin schrieb:

> TIMSK=0x02;
> Heißt nur, dass der Timer Interrupt aktiviert wird.


Das weiß ich selber auch. Aber bei

  TIMSK |= ( 1 << TOIE0 );

kann ich direkt im Code sehen, dass hier das '_T_imer _O_verflow 
_I_nterrupt _E_nable bit vom timer _0_' gesetzt wird. Da steht also 
(mehr oder weniger) im Klartext, das hier der Overflow freigegeben wird. 
Es gibt nämlich im allgemeinen Fall auch noch andere Interrupts als nur 
den Overflow, die von einem Timer ausgelöst werden können.

Ausserdem geht es nicht um die Freigabe des Overflows, sondern darum, 
wie schnell eigentlich der Timer getaktet wird.
Dazu braucht man 2 Angaben
* die Taktfrequenz des µC
* welcher Vorteiler wird benutzt

aus diesen Angaben kann man errechnen, wie schnell der Timer getaktet 
wird und damit, und dem Wissen wie weit er zählt, errechnet sich dann, 
wie groß der zeitliche Abstand zwischen den Overflows ist.

Die µC Taktfrequenz musst du wissen. Welcher Vorteiler eingestellt ist, 
ist hier
TCCR0B=0x02;
codiert.
Und ich hab jetzt keine Lust rauszusuchen, welche CS-Bits in TCCR0B 
stecken und was sie bedeuten. Du bist der Programmierer, das ist dein 
Job.

http://www.mikrocontroller.net/articles/FAQ#Timer

von ge-nka (Gast)


Lesenswert?

Eigentlich sagt dir der CV-AVR in Kommentaren wie schnell dein Timer 
läuft,
vorausgesetzt du hast danach im "c" Text nichts verändert.

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: 8bit top=FFh


// Clock value: 1000,000 kHz bedeutet, dass der Timer mit 1MHz betrieben 
wird,das weiß der Compiler natürlich nur aus der Angabe des Systemtakts 
und dem Teiler, also 8Mhz/8-Teiler =1MHz.

Natürlich kann der Compiler nicht wissen wie die Fuses eingestellt sind 
und wie schnell der Quarz taktet, also bei 16Mhz Takt wäre der Timer mit 
oberen Einstellungen mit 2MHz gelaufen also 2 mal schneller.

von Martin (Gast)


Lesenswert?

OK, mein Problem war/ist mit dem "volatile" doch nicht gelöst.

Hier die Kurzfassung:
Vor dem Interrupt = als globale Variable
1
volatile unsigned int zaehler;

Timer-Interrupt:
1
interrupt [TIM0_OVF] void timer0_ovf_isr(void) 
2
{
3
    zaehler++;     
4
    if(zaehler==400)  
5
    {   
6
        zaehler=0;
7
        //Selbsthaltung lösen...
8
    }                        
9
}
1
void main(void)
2
{...
3
#asm("sei")
4
while (1) {
5
    if(TASTER)
6
    {
7
        delay_us(10); 
8
        //Selbsthaltung aktivieren    
9
        #asm("CLI")      
10
        zaehler=0;
11
        #asm("sei")
12
    }
13
}

Warum geht das Gerät meist bei Tastendruck aus?!?!

von Krapao (Gast)


Lesenswert?

> Warum geht das Gerät meist bei Tastendruck aus?!?!

Das kann abartige Ursachen haben wie z.B. ein Kurzschluss durch die 
Tasterbetätigung und dadurch einen Reset (Power, Brownout) deiner ganzen 
Hardware. Denn nach dem Hochfahren des µC aus dem Reset heraus gilt ja 
auch PORTA.1=0;... Um diesen Fall zu Debuggen könntest du einen weiteren 
Portpin zu Beginn von main vor der while() Schleife schalten und mit 
einem Oszi oder einer LED überwachen, ob der µC resettet.

von ge-nka (Gast)


Lesenswert?

Ist bei dir der Eingang als Pulldown oder als Pullup geschaltet ?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.