Forum: Compiler & IDEs Input Capture Register steuern


von Elias 1. (alajas)


Angehängte Dateien:

Lesenswert?

Hallo,

ich nutze den USB Teensy 2++ für eine Ultraschall Messung.
Dabei habe ich folgendes Problem: Der Timer, welcher mir den Zeitabstand 
misst zwischen dem ansteigenden und fallenden Eingangsflanke, zeigt 
immer den selben Wert an.

Grundsätzlich ist die Idee eine Ansteigende Flanke mit dem IPC Pin 
(Register) zu erkennen. Danach soll ein Interrupt ausgelöst werden. Die 
Interrupt ruft eine Funktion auf signal_laengen_messung. Hier wird ein 
anderer Timer auf 0 resetet (dieser soll dann mitzählen). gleichzeit 
möchte ich das das Timer Register so umgeschaltet wird das er eine 
fallende Flanke erkennt. Somit sollte eine fallende Flanke den nächsten 
Interrupt auslösen. Ich glaube das hier der Hund begraben ist.
Der Timer Wert ist immer 6.

Die Problem Stelle:
1
int signal_laenge_messen() 
2
{
3
4
//überprüft ob das BIT auf 1 steht, im Timer2 Reg.  steigende Flanke am Eingang
5
if (TCCR1B |= (1<<ICES1)) 
6
{
7
//löscht das BIT im Timer2 Reg.  fallende Flanke am Eingang
8
TCCR1B &= ~(1<<ICES1);
9
TCNT2 = 0;   //Timer2 reseten
10
}
11
12
if (TCCR1B &= ~(1<<ICES1))
13
{
14
  i = TCNT2;
15
  printzahlen(i);
16
  
17
  print("\n");
18
  TCCR1B |= (1<<ICES1);
19
}
20
return 0;
21
}


MfG Elias






DER komplette Code:
1
  #include <avr/io.h>
2
#include <avr/pgmspace.h>
3
#include <util/delay.h>
4
#include "usb_debug_only.h"
5
#include "print.h"
6
#include <stdlib.h>
7
#include <stdio.h>
8
#include <avr/interrupt.h>
9
10
//Einstell Möglichkeiten für den CPU Takt
11
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
12
#define CPU_16MHz       0x00
13
#define CPU_8MHz        0x01
14
#define CPU_4MHz        0x02
15
#define CPU_2MHz        0x03
16
#define CPU_1MHz        0x04
17
#define CPU_500kHz      0x05
18
#define CPU_250kHz      0x06
19
#define CPU_125kHz      0x07
20
#define CPU_62kHz       0x08
21
22
//Funktionsprotypen
23
int empfangen (void);
24
int signal_laenge_messen(void); 
25
26
//Variabeln
27
int count0 = 0;
28
long signed int count1 = 0;
29
long signed int count2 = 0;
30
int count3 = 0;
31
32
char Puffer [10] = {10, 20, 30, 40, 50, 1};
33
unsigned int j= 0;
34
unsigned int x= 341;
35
36
volatile char T1lbyte [8];
37
volatile char T1hbyte [8];
38
volatile unsigned int i= 0;
39
  
40
41
42
int main (void)
43
44
{
45
  CPU_PRESCALE(CPU_8MHz); // set for 8 MHz clock, and make sure the LED is off
46
  usb_init();
47
  
48
  
49
  //Timer0 10us für senden
50
  //8000000 / 1 / 80 = 100000 Hz = 10us
51
  //PIN B7 = OC0A
52
  //Timer0 8Bit
53
  TCCR0A |= (1<<COM0A0 | 1<<WGM01); //Timer Control Register 0A, Toggelt PIN OC0A on capture compare, Timer Control Register A (CTC Mode)
54
  TCCR0B |= (0<<CS02 | 0<<CS01 | 1<<CS00); //Timer Control Register 0B (kein Prescaler)
55
  OCR0A = 100 ; //(255 max Wert 8 Bit Register, muss angegeben werden im ctc Mode)
56
  TIMSK0 |= (1<<OCIE0A | 0<<TOIE0); //Timer0 Interupt Mask (erlaubt Interupt bei Counter überlauf),
57
  //Wenn Reg. 0A zu einen compare match kommt kann ein Interrupt ausgelöst werdentims  
58
  
59
  //Timer1 wird für das empfangen der steigenden Flanke benützt
60
  //Timer1 = 16Bit
61
  //Pin ICP1 = D4
62
  TCCR1B|= (1<<ICNC1 | 1<<ICES1 | 0<<WGM13 | 0<<WGM12 | 0<<CS12 | 0<<CS11 | 0<<CS10); 
63
  //Input caputure nois cancler ist eingeschlatet, Input Capture Edge Select wird bei einer ansteigenden Flanke aktiviert
64
  TIMSK1 |= (1<<ICIE1);
65
    
66
  //Timer2 wird für das zählen des Empfangssignal verwendet
67
  //Timer = 8 Bit
68
  //TCCR2A |= ();
69
  TCCR2B |= (0<<CS22 | 0<<CS21 | 1<<CS20); //kein Prescaler, Timer wurde eingeschalten.
70
  
71
  DDRF &= ~(1<<PF0); //Setzt PINF0 auf Ausgang
72
  PORTF |= (1<<PF0); //schaltet den Pullup widerstand ein.
73
  PINF &= ~(1<<PINF0); //schaltet den PIN auf low
74
  
75
  while (1)
76
  {
77
  }
78
  return 0;
79
}
80
  
81
82
83
ISR (TIMER1_CAPT_vect) //löst einen Interrupt aus wenn ein Flankenanstieg beim Eingang F0 registriert wurde
84
{
85
signal_laenge_messen();
86
}
87
88
89
90
int signal_laenge_messen() 
91
{
92
93
//überprüft ob das BIT auf 1 steht, im Timer2 Reg.  steigende Flanke am Eingang
94
if (TCCR1B |= (1<<ICES1)) 
95
{
96
//löscht das BIT im Timer2 Reg.  fallende Flanke am Eingang
97
TCCR1B &= ~(1<<ICES1);
98
TCNT2 = 0;   //Timer2 reseten
99
}
100
101
if (TCCR1B &= ~(1<<ICES1))
102
{
103
  i = TCNT2;
104
  printzahlen(i);
105
  
106
  print("\n");
107
  TCCR1B |= (1<<ICES1);
108
}
109
return 0;
110
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Das 2.te if hier
1
int signal_laenge_messen() 
2
{
3
4
//überprüft ob das BIT auf 1 steht, im Timer2 Reg.  steigende Flanke am Eingang
5
if (TCCR1B |= (1<<ICES1)) 
6
{
7
//löscht das BIT im Timer2 Reg.  fallende Flanke am Eingang
8
TCCR1B &= ~(1<<ICES1);
9
TCNT2 = 0;   //Timer2 reseten
10
}
11
12
if (TCCR1B &= ~(1<<ICES1))
13
{
14
  i = TCNT2;
15
  printzahlen(i);
16
  
17
  print("\n");
18
  TCCR1B |= (1<<ICES1);
19
}
20
return 0;
21
}

kannst du dir sparen.
Generell solltest du mehr 'else' einsetzen, wenn es nur 2 Möglichkeiten 
gibt. Ein Bit kann entweder 1 oder 0 sein. Wenn es nicht 1 ist, dann 
MUSS es 0 sein. Es gibt keine andere Möglichkeit.

Gleichzeitig liegt hier auch dein Problem
Du hast geschrieben
1
   if( Bit gleich 0 )
2
     setzte Bit auf 1
3
4
   if( Bit auf 1 )
5
     mach was

Bei der 2-ten Abfrage ist dein Bit IMMER auf 1. Denn entweder ist das 
Bit von Anfang an schon auf 1, oder du setzt es auf 1, wenn es das nicht 
war. Damit ergibt die 2-te Abfrage IMMER true.

Was du eigentlich wolltest
1
    if( Bit gleich 0 )
2
      setzte Bit auf 1
3
4
    else
5
      mach was


Solche Abfragen auf 2 Möglichkeiten sind meistens eine schlechte Idee
1
  if( x < 5 )
2
     ...    // x wird hier nicht verändert
3
4
  if( x >= 5 )
5
     ...
1
  if( i < 10 )
2
    ...
3
  if( i >= 10 && i < 20 )
4
    ...
5
  if( i >= 20 )
6
    ...

x kann nur kleiner oder größer gleich 5 sein. Wenn es nicht kleiner ist, 
dann ist es automatisch größer/gleich. Was soll es denn sonst sein? 
Derartige 'Doppelabfragen' sind deshalb eine schlechte Idee, weil man 
dann die Logik der Abfrage 2 mal im Code hat und beide Varianten das 
Gegenteil aussagen müssen. Das ist erstens nur Schreibarbeit für nichts 
und zweitens eine weitere Möglichkeit Fehler zu machen. Bei dir kommt 
dann  noch dazu, dass du dann innerhalb der jeweilige Alternative dann 
auch noch die Grundlage der Abfrage veränderst (die 'Variable oder das 
Register).
1
  if( x < 5 )
2
    ...
3
  else
4
    ...
1
  if( i < 10 )
2
    ...
3
  else if( i < 20 )
4
    ...        // i kann hier nicht mehr kleiner als 10 sein
5
               // denn wenn es das wäre, dann wäre das Programm nie hier
6
               // her gekommen, sondern der erste if wäre genommen worden
7
  else
8
    ...        // i kann hier nicht mehr kleiner als 20 sein (oder 10)
9
               // wieder: wenn es das wäre, dann wäre das Programm nie
10
               // an diese Stelle gekommen

Manchmal ist es also klug, nicht zuuuu explizit zu sein, sondern sich zu 
überlegen, welche Dinge durch vorhergehende Abfragen schon 
ausgeschlossen sind und diese Dinge implizit mitzubenutzen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

IM übrigen erhebt sich die Frage:
Was soll das werden? Wozu 2 Timer benutzen?

Der Clou beim Input Capture besteht darin, dass der Timer 1 beide 
Aufgaben gleichzeitig übernimmt. Er zählt und wenn am Input Capture Pin 
ein Ereignis auftritt, dann sichert die Hardware den aktuellen 
Zählerstand sofort und löst den Interrupt aus. D.h. das ist das genauest 
mögliche Zählergebnis. Alles was du tun musst ist, die Zählerstände 
voneinander abziehen und du hast die Anzahl der Timerticks zwischen den 
beiden Ereignissen
1
uint16_t startCount;
2
3
ISR( Timer Capture )
4
{
5
  if( Flanke == ansteigend ) {
6
    startCount = ICR1;
7
    Flanke umstellen;
8
  }
9
10
  else {
11
    Timerticks = ICR1 - startCount;
12
    Flanke umstellen;
13
    Ergebnis Timerticks bearbeiten;
14
  }
15
}

wozu soll das gut sein, einen 2.ten Timer zu benutzen? Manchmal muss man 
eben ein wenig rechnen. Machst du doch im realen Leben auch, wenn du 
eine Zeit stoppen hast und keine Stoppuhr sondern nur eine Uhr mit 
Sekundezeiger hast. Du merkst dir, wo der Sekundenzeiger am Anfang 
stand, und beim 2-ten Ereignis liest du den Sekundenzeiger wieder ab und 
die Differenz eergibt dir die Sekunden dazwischen.

PS: Den Teil 'Timerticks bearbeiten' willst du nicht wirklich in der ISR 
haben. Du setzt dir ein Flag und bearbeitest dann das Ergebnis gemtlich 
in der Hautpschleife.

Such mal nach "Frequenzzähler" hier im Forum. Es gibt zahlreiche 
Threads, die dir zeigen, wie man das richtig macht.

: Bearbeitet durch User
von Oliver (Gast)


Lesenswert?

Noch ein Hinweis aus den Datenblatt zum Umschalten der Flankenrichtung 
bei ICP:

"After a change of the edge, the Input Capture Flag (ICFn) must be
cleared by software (writing a logical one to the I/O bit location)."

Oliver

von Stefan E. (sternst)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das 2.te if hier
> ...
> kannst du dir sparen.

Darüber hinaus sind die "Bedingungen" beider ifs natürlich Banane. ;-)

von Elias 1. (alajas)


Lesenswert?

Danke für den Tipp,

ich wusste um ehrlich zu sein nicht nach was ich suchen sollte hier im 
Forum.
Werde es so mal ausprobieren ob es funktioniert. Ich werde jedoch die 
ganzen Arbeitsschritte auslagern, sprich, die IR startet nur ein 
Programm welche das dann abhandelt.

MfG Elias

von Elias 1. (alajas)


Lesenswert?

Stefan Ernst schrieb:
> Karl Heinz Buchegger schrieb:
>> Das 2.te if hier
>> ...
>> kannst du dir sparen.
>
> Darüber hinaus sind die "Bedingungen" beider ifs natürlich Banane. ;-)


Manchmal genügt 1mal, Danke.

von Elias 1. (alajas)


Lesenswert?

Oliver schrieb:
> Noch ein Hinweis aus den Datenblatt zum Umschalten der Flankenrichtung
> bei ICP:
>
> "After a change of the edge, the Input Capture Flag (ICFn) must be
> cleared by software (writing a logical one to the I/O bit location)."
>
> Oliver


Danke für den Hinweis!!!

von Marius W. (mw1987)


Lesenswert?

Elias 1234 schrieb:
> if (TCCR1B |= (1<<ICES1))
> ...
> if (TCCR1B &= ~(1<<ICES1))


Falls es noch niemandem aufgefallen ist: Die if-Abfrage ist generell 
Müll. Denn die Bedingung ist eigentlich gar keine Bedingung sondern ein 
Bit-Setzen/-Rücksetzen-Befehl. Da sollte man sich nochmal Gedanken 
machen, wie man einzelne Bits abfragt...

Gruß
Marius

EDIT: siehe 
http://www.mikrocontroller.net/articles/Bitmanipulation#Bits_pr.C3.BCfen

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Marius Wensing schrieb:

> Falls es noch niemandem aufgefallen ist: Die if-Abfrage ist generell
> Müll. Denn die Bedingung ist eigentlich gar keine Bedingung sondern ein
> Bit-Setzen/-Rücksetzen-Befehl. Da sollte man sich nochmal Gedanken
> machen, wie man einzelne Bits abfragt...

Da kann man mal sehen, wie betriebsblind man mit der Zeit wird.
Ist tatsächlich nicht aufgefallen.

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.