Forum: Mikrocontroller und Digitale Elektronik Timerwert erfassen


von Mark Z. (lyams)


Lesenswert?

Hallo,
Also was ich vorhabe ist eigentlich trivial, dennoch treibt es mich fast 
in den Wahnsinn.
Ich will eine Frequenz messen, genauer gesagt eine Periodendauer.
Ich lege eine Frequenz an meinen Mikrocontroller (ICP1)
dort sollte ja der Timer 1 mit einer Auflösung von 500ns (16MHz mit 
Prescaler 8) die Zeit zwischen 2 Flanken messen.

Die Ausgabe ist hier nicht zu beachten, habe ich kurzfristig verändert. 
weil mich das ergebnis verwundert hat. Hier sollte man ja eig 0 erwarten 
wenn am Eingang ne gleichbleibende Frequenz anliegt.
die Bedingung ist allerdings mal erfüllt und mal nich.

Ich weiß nicht warum ich einfach nicht das erwartete Ergebnis bekommen.
Was ist falsch?
Ich bitte um Hilfe, is sicher iwo ein grobes Verständnisproblem 
meinerseits.
Habe ich gestern schon versucht und die Tipps von einem Moderator 
umgesetzt, es hilft aber nix.

hier mein Code:
1
void Interrupt_init()
2
  {
3
    sei();          
4
    GICR |= (1<<INT1);
5
    GIFR = (INTF1);
6
    TIMSK |= (1<<TICIE1);  
7
    TCCR1A = 0x00;
8
    TCCR1B = (1<<ICNC1) | (1<<ICES1) | (1<<CS11) | (0<<CS10);
9
                              
10
  }
11
 
12
void Ausgabe()
13
{
14
   while(1)
15
   {
16
     PORTA=0x00;      //Startsignal - alle LED an            
17
     _delay_ms(1000);
18
       }
19
    if (u8_timerwert[1]-u8_timerwert[0]<90)
20
    {
21
      PORTA=0b00000101;
22
      _delay_ms(400);
23
    }
24
    else
25
    {
26
      PORTA=0b00000001;
27
      _delay_ms(400);
28
    }
29
     
30
   }
31
 }
32
33
volatile uint8_t flag;
34
volatile uint8_t i=0;
35
ISR(TIMER1_CAPT_vect)
36
 {    
37
                   
38
  if (i>0)            //ersten Wert verwerfen            
39
  {      
40
  u8_timerwert[i-1]=ICR1;    //Array mit Timerwerten beschreiben  
41
  }  
42
  i++;
43
  if (i==3)            //Abbruchbedingung              
44
  {  
45
      GICR &= ~(1<<INT1);    //ISR deaktivieren
46
      flag=1;          
47
  }
48
                
49
  
50
}
51
 
52
int main(void)
53
{
54
  DDRA=0xff;
55
  Interrupt_init();
56
  while(1)
57
  {
58
     if (flag==1)
59
     {
60
       Ausgabe();
61
     }
62
  }
63
}

von Stefan (Gast)


Lesenswert?

Wenn Du zwischen dem lesen von zwei Timerwerten durch einen Interrupt 
unterbrochen wirst, erhälst Du zwei Werte, die nicht mehr zusammen 
passen.

Du musst beim Lesen Interrupts sperren:

cli();
uint8_t a=u8_timerwert[1];
uint8_t b=u8_timerwert[0];
sei();
if (a-b<90) ...

von Mark Z. (lyams)


Lesenswert?

Ok, aber bei meiner derzeitig verwendeten Frequenz von 20,8kHz sollte 
das doch zeitunkritisch sein oder?
Ich habe dabei 48µs Zeit bis zum nächten Interrupt, und was ich während 
dessen mache sollte doch nicht so lang dauern.
Oder verstehe ich hier etwas falsch?

Danke trotzdem für den Rat, ich versuch natürlich das umzusetzen.
sperrt denn cli(); wirklich in jedem Fall den Interrupt?

von Uwe (de0508)


Lesenswert?

Hallo Mark,

"CLI"
ja, das ist Grundlagenwissen.

Tipp schau dir auch mal alle Assemblerbefehle eines Atmel µC an.

http://www.atmel.com/images/doc0856.pdf‎

von ich (Gast)


Lesenswert?

1
cli();
ist in Ordnung, aber
1
sei();
würde ich nicht machen. Wenn nämlich vor dem Aufruf die Interrupts schon 
gesperrt waren, werden sie hier mit sei(); auf jeden Fall freigegeben. 
Das heißt, daß nicht der vorherige Zustand wiederhergestellt wird.

Besser ist es so:
1
uint8_t sreg;
2
sreg = SREG;
3
cli();
4
...
5
...
6
SREG = sreg;

Dann wird nach dem Vorgang der vorherige Zustand wieder hergestellt.

von Stefan (Gast)


Lesenswert?

> Ich habe dabei 48µs Zeit bis zum nächten Interrupt

Du sollst die Interrups im Hauptprogramm sperren. Da dieses keine 
Rücksicht auf die Zeitpunkte der Interrupts nimmt, kann ein Interrupt 
zwischen dem lesen der zwei Werte stören.

Die Interrupt-Routine ist schon Ok - jedefalls auf den ersten Blick.

von Uwe (Gast)


Lesenswert?

>     GIFR = (INTF1);
Sieht komisch aus

>void Interrupt_init()
>  {
>    GICR |= (1<<INT1);
>    GIFR = (INTF1);
>    TIMSK |= (1<<TICIE1);
>    TCCR1A = 0x00;
>    TCCR1B = (1<<ICNC1) | (1<<ICES1) | (1<<CS11) | (0<<CS10);
>    sei();
>  }
"sei()" würde ich erst am Ende machen

>volatile uint8_t flag;
Könnte man zur Sicherheit noch initialisieren
volatile uint8_t flag=0;

von Mark Z. (lyams)


Lesenswert?

Habs grad mal mit dem Oszi gemessen, also meine ISR läuft nur 1,6µs, das 
heißt ja ein neuer Interrupt kommt mir während der Zeit der Verarbeitung 
nicht dazwischen.
Aber mach ich vlt iwo beim setzen der Bits in den Registern was falsch?
Ich habe jetzt eine Frequenz von 20,8kHz anliegen und der Timer auf der 
Empfängerseite läuft mit 2MHz (16MHz mit Prescaler 8)
also sollte doch ein Timerwert von 96=T1/T2 zu erwarten sein oder 
zumindest 95-97.

von Mark Z. (lyams)


Lesenswert?

Ich bekomm hier gleich ein Magengeschwür, also ehrlich.
cli(); stoppt den Interruptbetrieb nicht.
erst das bit INT0 im Register GICR 0 zu setzen stoppt meinen Interrupt.
Oooooooh man, das hat Nerven gekostet.
Hat vlt noch jemand ne plausible Erklärung dafür?
Einfach sowas wie, Input Capture Interrupt läuft unabhängig vom SREG und 
das mit Beleg im Datenblatt?
Das wär Balsam für meine Seele!

Wer es testen möchte, hier der Code:
1
void Ausgabe(void)
2
{  
3
    PORTA=0xff;
4
    _delay_ms(200);
5
    PORTA=0x00;
6
    _delay_ms(200);
7
}
8
9
volatile uint8_t flag;
10
volatile uint8_t i=0;
11
12
 ISR(TIMER1_CAPT_vect)
13
 {                
14
  if (i>0)              
15
  {      
16
    u8_timerwert[i-1]=ICR1;  
17
  }  
18
  i++;
19
  if (i==3)              
20
  {    
21
    GICR= (0<<INT1); //ohne das gehts nicht!!!
22
    cli();      
23
    flag=1;    
24
  }
25
  TCNT1L=0x00;              
26
}

von Thomas E. (thomase)


Lesenswert?


von Dietrich L. (dietrichl)


Lesenswert?

Mark Ziegler schrieb:
> cli(); stoppt den Interruptbetrieb nicht.

In einer ISR ist der Interrupt sowieso disabled, d.h. beim Anspringen 
der ISR wird "cli" quasi von der Hardware ausgeführt.

Das nochmalige "cli" innerhalb der ISR ändert also nichts. Aber beim 
Verlassen der ISR wir der Interrupt in jedem Fall wieder eingeschaltet - 
auch wenn Du das nicht ins Programm schreibst.

Gruß Dietrich

von Mark Z. (lyams)


Lesenswert?

Vielen vielen Dank!
...damit ist es klar:)

von Sascha W. (sascha-w)


Lesenswert?

@Mark Ziegler

was machst du überhaupt mit INT1? Der hat mit dem Inputcapture des 
Timers nichts zu tun!
Exsistiert dafür irgendwo eine ISR?

Sascha

von ich (Gast)


Lesenswert?

> Aber beim Verlassen der ISR wir der Interrupt in jedem Fall wieder
> eingeschaltet - auch wenn Du das nicht ins Programm schreibst.

Falsch!
Beim Verlassen der ISR wird der Zustand wieder hergestellt, der 
herrschte, als die ISR betreten wurde. Das heißt, wenn er vorher schon 
ausgeschaltet war, ist er auch nach Verlassen der ISR ausgeschaltet.

von Sascha W. (sascha-w)


Lesenswert?

ich schrieb:
>> Aber beim Verlassen der ISR wir der Interrupt in jedem Fall wieder
>> eingeschaltet - auch wenn Du das nicht ins Programm schreibst.
>
> Falsch!
> Beim Verlassen der ISR wird der Zustand wieder hergestellt, der
> herrschte, als die ISR betreten wurde. Das heißt, wenn er vorher schon
> ausgeschaltet war, ist er auch nach Verlassen der ISR ausgeschaltet.
auch Falsch

wenn er vorher schon ausgeschaltet war, wird die ISR auch nicht 
aufgerufen.

Sascha

von ich (Gast)


Lesenswert?

Stimmt. ->Hand an die Stirn :-))

von Route_66 (Gast)


Lesenswert?

Sascha Weber schrieb:
> wenn er vorher schon ausgeschaltet war, wird die ISR auch nicht
> aufgerufen.

Es sei denn, ein verirrter Prozessor kommt auf einem anderen Wege in die 
ISR - z.B. durch einen Stack-Fehler.

von Thomas E. (thomase)


Lesenswert?

Route_66 schrieb:
> Sascha Weber schrieb:
>> wenn er vorher schon ausgeschaltet war, wird die ISR auch nicht
>> aufgerufen.
>
> Es sei denn, ein verirrter Prozessor kommt auf einem anderen Wege in die
> ISR - z.B. durch einen Stack-Fehler.
Und beim Verlassen werden die Interrupts global eingeschaltet.

Denn diesen Unsinn macht er nicht:

ich schrieb:
 > Falsch!
> Beim Verlassen der ISR wird der Zustand wieder hergestellt, der
> herrschte, als die ISR betreten wurde. Das heißt, wenn er vorher schon
> ausgeschaltet war, ist er auch nach Verlassen der ISR ausgeschaltet.

mfg.

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.