Timerwert erfassen

OP #3280182
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
}
Gast #3280194
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) ...
OP #3280213
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?
Gast #3280238
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.
Gast #3280255
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.
Gast #3280265
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;
OP #3280266
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.
OP #3280345
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
}
#3280387
Lesenswert?

#3280397
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
Gast #3280486
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.
#3280502
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
#3281171
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.

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