Forum: Mikrocontroller und Digitale Elektronik ATmega328 Timer 1 Input capture auf 32 Bit verlängern


von Michael K. (k-mte)


Lesenswert?

Hallo Forum,

ich möchte mit einem ATmega328 die 16 Bit Input Capture-Funktion von 
Timer 1 nutzen. Dies geht bei einer Taktfrequenz von 16 MHz bis 2^16 / 
16 MHZ = ca. 4 ms auch ohne Timer 1 Overflow-Interrupts. Bei längeren 
Zeiten muß man die Timer 1 Overflows in die Berechnung der Zeitdauer mit 
einbeziehen.

Das Problem ist, das zwischen dem Auslesen des Capture-Werts von Timer 1 
und dem Auslesen der Überläufe von Timer 1 Zeit vergeht, in der ein 
weiterer Überlauf von Timer 1 stattgefunden haben kann.

Frage: wie löst man dieses Problem technisch sauber?

Ich würde mich über Beispiel-Code freuen.

Viele Grüße

k-mte

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn man es richtig macht geht nichts verloren. Das zählen erfolgt 
sozusagen "intern" im Hintergrund. Du kannst aller X [ms] oder [s] oder 
[...] den gerade aktuellen Zustand/Wert auslesen.

Stichworte volatile und atomar/atomic.

von Carl D. (jcw2)


Lesenswert?

Michael K. schrieb:
> Hallo Forum,
>
> ich möchte mit einem ATmega328 die 16 Bit Input Capture-Funktion von
> Timer 1 nutzen. Dies geht bei einer Taktfrequenz von 16 MHz bis 2^16 /
> 16 MHZ = ca. 4 ms auch ohne Timer 1 Overflow-Interrupts. Bei längeren
> Zeiten muß man die Timer 1 Overflows in die Berechnung der Zeitdauer mit
> einbeziehen.
>
> Das Problem ist, das zwischen dem Auslesen des Capture-Werts von Timer 1
> und dem Auslesen der Überläufe von Timer 1 Zeit vergeht, in der ein
> weiterer Überlauf von Timer 1 stattgefunden haben kann.

Wenn die Timer1-Capture-ISR verwendet wird, dann kann der 
Timer1-Overflow gar nichts anstellen, da er niedrigere Prio hat. Solange 
man in der Capture-ISR nicht vor dem Lesen von Capture-Register und 
Overflow-Variable die Interrupts wieder freigibt (und falls du fragen 
würdest, wie das geht, ist das unwahrscheinlich), dann kann nichts 
passieren.

Ohne Capture-ISR, also z.B. in der Main-/Arduino-Loop, sollte man 
ICP1-Wert und TCNT1-Wert vergleichen, ist letzterer kleiner, dann hat 
man einen Überlauf zu viel gezählt.

> Frage: wie löst man dieses Problem technisch sauber?
>
> Ich würde mich über Beispiel-Code freuen.
>
> Viele Grüße
>
> k-mte

von S. Landolt (Gast)


Lesenswert?

> ein weiterer Überlauf von Timer 1

Der steht dann ja im (nicht gelöschten) TIFR1.TOV1.

von Oliver S. (oliverso)


Lesenswert?

Michael K. schrieb:
> Das Problem ist, das zwischen dem Auslesen des Capture-Werts von Timer 1
> und dem Auslesen der Überläufe von Timer 1 Zeit vergeht, in der ein
> weiterer Überlauf von Timer 1 stattgefunden haben kann.

Auf das Capture-Flag in der OVF-ISR prüfen. Wenn das da gesetzt ist, ist 
das der Hinweis dafür, daß der zu zählende Überlauf entweder 
gleichzeitig mit dem (dann ist der Capture-Wert entweder MAX oder 
BOTTOM, je nach Zählerbetriebsart) oder nach dem Capture-Event 
stattgefunden hat.

Oliver

von Thomas E. (thomase)


Lesenswert?

Michael K. schrieb:
> Frage: wie löst man dieses Problem technisch sauber?

Stell den Prescaler so ein, daß es in einem Umlauf passt. Wozu brauchst 
du bei solch einer Langzeitmessung eine Auflösung von 62,5ns?

von Sebastian S. (amateur)


Lesenswert?

>Michael K. schrieb:
>> Frage: wie löst man dieses Problem technisch sauber?

>Stell den Prescaler so ein, daß es in einem Umlauf passt. Wozu brauchst
>du bei solch einer Langzeitmessung eine Auflösung von 62,5ns?

@Thomas E.
Merke:
In sehr vielen Posts hier im Forum kann man sehen, dass z. B. bei großen 
Öfen 1000 mal pro Sekunde die Temperatur abgefragt wird und der 
zugehörige Regler damit gefüttert wird, der Pegel in einem großen 
Wasserbecken, via Ultraschall (Kann man das nicht mit einem Laser noch 
schneller machen?), so schnell wie möglich abgefragt wird und die 
Motorposition eines tonnenschweren Teils 50000 mal pro Sekunde ermittelt 
wird.
Oft mit dem Ende vom Lied: Ich brauche einen schnelleren Prozessor!

von Thomas E. (thomase)


Lesenswert?

Sebastian S. schrieb:
> @Thomas E.
> Merke:

Ich werd's mir hinter die Ohren schreiben.

von S. Landolt (Gast)


Lesenswert?

an Michael K.:

Ich sehe es so: in der ICP-ISR stellt sich die Frage, ob ein noch nicht 
gezählter Timer-Overflow stattgefunden hat. Dazu prüft man das TOV-Flag: 
ist es gesetzt, muss noch festgestellt werden, wann das geschah, ob vor 
oder nach dem ICP: liegt der ICR-Wert knapp unter TOP, wird der 
Overflow verworfen, liegt er knapp über BOTTOM, muss der Overflow 
hinzuaddiert werden.

von m.n. (Gast)


Lesenswert?

Michael K. schrieb:
> Ich würde mich über Beispiel-Code freuen.
1
// Routinen zur Erfassung der Eingangsimpulse
2
// Ueberlaeufe von T1
3
ISR(TIM1_OVF_vect)            
4
{
5
  ueberlauf++;                            // Ueberlaeufe von T1 ergeben obere 16bit der Messzeit
6
}
7
8
// CAPT-Ereignisse an AIN1
9
ISR(TIM1_CAPT_vect)                       // Eingangsimpulse mit genauem Zeitpunkt erfassen
10
{
11
static unsigned long count;               // Impulse per Interrupt                
12
  count++;
13
  if(mess_status == AUSLESEN) {           // Ergebnisse synchron zum Eingangsimpuls auslesen+ablegen
14
    end_ereignis = count;                 // Anzahl der Impulse lesen
15
    zeit_low = ICR1;                      // capture-reg lesen: untere 16bit
16
    zeit_high = ueberlauf;                // dazu die oberen 16bit
17
    if((TIFR1 & BIT(TOV1)) && 
18
      (zeit_low < T1_MAX/2))              // evtl. Ueberlauf T1 noch offen?
19
      zeit_high++;                        // nur, wenn capture-int + overflow-int gleichzeitig !
20
    mess_status = AUSWERTEN;              // Daten fertig fuer Auswertung
21
  }
22
}

von Michael K. (k-mte)


Angehängte Dateien:

Lesenswert?

Hallo Forum,

ich habe mittlerweile ein avr-gcc-Programm erstellt, das mittels Input 
Capture die Timer 1-Ticks zwischen zwei steigenden Flanken eines 
TTL-Signals zählt, das an Pin ICP1 (= Pin 14) eines ATmega328P anliegt. 
Wenn dort eine Frequenz von 1 Hz einspeise, werden wie erwartet 16 Mio. 
Ticks gezählt. Das Programm scheint also zu funktionieren. Ich würde 
mich freuen, wenn dennoch einmal erfahrene Forumsmitglieder den Code 
anschauen und auf offensichtliche Fehler überprüfen könnten, damit 
jemand, die diesen Code möglicherweise in Zukunft einmal kopiert, keine 
Probleme damit bekommt.

Vielen Dank!

MfG

k-mte
1
#include <stdio.h>
2
#include <stdint.h>
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
7
#define BAUD 38400UL
8
9
#define MYUBRR (((F_CPU + BAUD * 8LU) / (BAUD * 16LU) - 1LU))
10
11
static FILE mystdout = { 0 };
12
13
volatile unsigned long t1_ticks = 0;
14
15
volatile unsigned char flag = 0;
16
17
/********************************************************************************/
18
19
static int uart_putchar (char c, FILE *stream)
20
  {
21
  while ( !( UCSR0A & (1<<UDRE0)) )
22
    ;
23
24
  UDR0 = c;
25
26
  return 0;
27
  }
28
29
/********************************************************************************/
30
31
void uart_init (void)
32
  {
33
  DDRD |= 0b10000000;
34
35
  UBRR0H = (unsigned char) (MYUBRR >> 8);
36
37
  UBRR0L = (unsigned char) (MYUBRR & 0xFF);
38
39
  UCSR0B = (1<<RXEN0) | (1<<TXEN0);
40
41
  UCSR0C = (3 << UCSZ00);
42
43
  fdev_setup_stream (&mystdout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
44
45
  stdout = &mystdout;
46
  }
47
48
/********************************************************************************/
49
50
ISR (TIMER1_OVF_vect)
51
  {
52
  t1_ticks += 0x10000;
53
  }
54
55
/********************************************************************************/
56
57
ISR (TIMER1_CAPT_vect)
58
  {
59
  unsigned char sreg = SREG;
60
61
  unsigned int icr1;
62
63
  cli ();
64
65
  icr1 = ICR1;
66
67
  if ((TIFR1 & (1<<TOV1)) && (icr1 < 0x8000))
68
    t1_ticks += 0x10000;
69
70
  t1_ticks += (unsigned long) icr1;
71
72
  flag = 1;
73
74
  SREG = sreg;
75
  }
76
77
/********************************************************************************/
78
79
void t1_init (void)
80
  {
81
  /** Input capture Pin ICP1 (Pin 14) = Eingang **/
82
83
  DDRB &= 0b11111110;
84
85
  /** PRR – Power Reduction Register (S. 54) **/
86
87
  PRR &= 0b11110111;
88
89
  /** TCCR1A – Timer/Counter1 Control Register A (S. 140) **/
90
91
  TCCR1A = 0;
92
93
  /** TCCR1B – Timer/Counter1 Control Register B (S. 142) **/
94
95
  TCCR1B = 0b11000001;
96
97
  /** TIMSK1 – Timer/Counter1 Interrupt Mask Register (S. 144) **/
98
99
  TIMSK1 = 0b00100001;
100
101
  /*** TIFR1 – Timer/Counter1 Interrupt Flag Register (S. 145) **/
102
103
  TIFR1 &= 0b00100111;
104
105
  /** Interrupts global ein **/
106
107
  sei ();
108
109
  }
110
111
/********************************************************************************/
112
113
int main (void)
114
  {
115
  uart_init ();
116
117
  t1_init ();
118
119
  while (1)
120
    {
121
    while (flag == 0)
122
      ;
123
124
    printf ("%lu\r\n", t1_ticks);
125
126
    t1_ticks = 0;
127
128
    flag = 0;
129
    }
130
131
  return 0;
132
  }

: Bearbeitet durch User
von Fachlaie (Gast)


Lesenswert?

Michael K. schrieb:
> TCCR1B = 0b11000001;

Ich liiieeeebe diese Magic Numbers.

Es gibt doch nichts schöneres als Rätsel zu lösen in denen
ein Rätsel versteckt ist.

von weg mit dem Troll - subito (Gast)


Lesenswert?

Ich verwende eine variable  "byte overflowcount"

Welcher beim Initialisieren auf Null gesetzt wird. Der overflow 
interrupr incrementiert diesen wert. Und beim naechsten capture wird zur 
Endzeit die 65536 * overflowcount hinzugezehlt.

von Michael K. (k-mte)


Lesenswert?

ACHTUNG, FEHLER IN DER ZIP-DATEI!

In der zip-Datei heißt es:
1
  if ((TIFR1 & (1<<TOV1)) && (icr1 < 0x8000))
2
    t1_ticks += 0x1000;

Richtig müßte es heißen:
1
  if ((TIFR1 & (1<<TOV1)) && (icr1 < 0x8000))
2
    t1_ticks += 0x10000;

(Konstante ist in der zip-Datei fälschlicherweise 0x1000 statt 0x10000)

Sorry!

k-mte

von Fachlaie (Gast)


Lesenswert?

Michael K. schrieb:
> Sorry!

Dass du hier die geneigten Interessenten mit (Aufforderungen
zum Datenblatt lesen) Rätseln bzw. Magic Numbers beaufschlagst
tut dir nicht leid?

von Peter D. (peda)


Lesenswert?

Michael K. schrieb:
> Frage: wie löst man dieses Problem technisch sauber?
>
> Ich würde mich über Beispiel-Code freuen.

Bitte sehr:

Beitrag "AVR Timer mit 32 Bit"

von m.n. (Gast)


Lesenswert?

Peter D. schrieb:
> Bitte sehr:

Danke, nein.
Zum einen kein Capture, zum anderen (verlangsamender) Prescaler 
aktiviert und zum 3. get_ticks() muß zyklisch aufgerufen werden, damit 
Überläufe von t2_soft wahrgenommen werden.

Michael K. schrieb:
> cli ();

Was soll das?

> ISR (TIMER1_OVF_vect)
>   {
>   t1_ticks += 0x10000;
>   }

Auf einem 8-Bitter eher ungeschickt, 0x10000 zu addieren. += 1 reicht 
aus.

von Hans-jürgen H. (hjherbert) Benutzerseite


Lesenswert?

Wir merken uns Global:


#define u32 unsigned long
#define u16 unsigned short
#define u16 unsigned char


u16 lastcnt1 ;       // Der zuletzt gelesene Wert
u16 actcnt1 ;      // Der jetzt aktuelle Wert
u16 countextender ;




Und wenn ich den Wert brauche, rufe ich dieses Unterprogramm:

FUNCT u32 gettick( void )                               // AVR
{
u8 oldstat ;
union {
        u32 ul ;
        struct {
                u16 lw ;
                u16 hw ;
                } w ;
        } retval ;
oldstat = SREG ;


cli();      // Interrupt ausschalten

actcnt1 = TCNT1 ;       // Den 16-Bit-Wert von das Hardware ( 2MHz )
if ( actcnt1 < lastcnt1 )
        {               // Einmal pro 65536 * 0.5 us Einmal alle 32768 
us
        ++countextender ;
        }
retval.w.lw = lastcnt1 = actcnt1 ;
retval.w.hw = countextender ;
SREG = oldstat ;
return retval.ul ;
}

Wenn ich dieses Unterprogramm gettick() in der Haupt-Arbeitsschleife 
garantiert alle 32768 us aufrufe, brauche ich keinen Interrupt.

Ansonsten muss in der Interrupt-Routine das gemacht werden:


cli();      // Interrupt ausschalten

actcnt1 = TCNT1 ;       // Den 16-Bit-Wert von das Hardware ( 2MHz )
if ( actcnt1 < lastcnt1 )
        {               // Einmal pro 65536 * 0.5 us Einmal alle 32768 
us
        ++countextender ;
        }
SREG = oldstat ;




Initialisiert wird T1 so:

// Einrichten des Zeitgebers
FUNCT inline void tickopen(void)    // #ifdef AVR
{
// Alle Atmel AVR Mikrocontroller
TCCR1A = 0 ;           // Bit 7,6 : no connection to output line
                       // Bit 1,0 : no PWM operation
TCCR1B = AVR_TIMER_DIVIDER_CODE ;       //
                       // Bit 7 : 0 = No Input noise capture
                       // Bit 6 : Edge Select
                       // Bit 3 : 0 = No Clear on compare
                       // Bit 2,1,0 : 000 = Stopped
                       //                         001 = CK/1
                       //                         010 = CK/8
                       //                         011 = CK/64 <<<
                       //                         100 = CK/256
                       //                         101 = CK/1024
                       //                         110 = extern H-L
                       //                         111 = extern L_H
}


Das Umrechnen von Verzögerungszeiten in Ticks geht durch das Makro:


#define sectotickx( secs ) (u32)( (secs) * tickpersec + 0.49999900 )

Der dafür benötigte Wert tickpersec ist fest, wird also schon vom 
Compiler berechnet:


#ifndef AVR_TIMER_DIVIDER
#if AVR_TIMER_DIVIDER_CODE == 1
#define AVR_TIMER_DIVIDER 1L
#elif AVR_TIMER_DIVIDER_CODE == 2
#define AVR_TIMER_DIVIDER 8L
#elif AVR_TIMER_DIVIDER_CODE == 3
#define AVR_TIMER_DIVIDER 64L
#elif AVR_TIMER_DIVIDER_CODE == 4
#define AVR_TIMER_DIVIDER 256L
#elif AVR_TIMER_DIVIDER_CODE == 5
#define AVR_TIMER_DIVIDER 1024L
#endif
#endif

#define tickpersec ((double)(F_CPU)/(double)AVR_TIMER_DIVIDER)  // 
20000000/8=2500000


Und das ganze habe ich so ähnlich nochmal für TurboC++ und DOS, um die 
Steuerung (oder Teile) im PC vortesten zu können.
Aber da stattdessen

#define tickpersec 1193180.0

struct stickuchars {
        u8 ucl ;
        u8 uch ;
        u16 uwh ; } ;
struct stickuwords {
        u16 uwl ;
        u16 uwh ; } ;
struct stickulongs {
        u32 ull ;
        } ;

union uticks {
        struct stickulongs ul ;
        struct stickuwords uw ;
        struct stickuchars uc ;
                } ;

u16 far * const tick_in_dos_page_46 = (u16 far *)0x0000046C ;
u16 far * const hitick_in_dos_page_46 = (u16 far *)0x0000046E ;


#define ul_mod_tick_in_dos_page_46      (*(u32 far *)0x0000046C)
// #endif
// ... continued #if defined( BORDOS ) || defined( DOSGNU )

static union uticks pre ;
static union uticks post ;
static u16 olddistick ;

FUNCT inline void tickopen( void )
{
disable();
outportb( 0x43, 0x34 );
// 0x34 = 0b00110100
//          00           Select Counter 0
//            11         Reload timer lsb msb
//              010      Mode 2 Rate Generator
//                 0     Binary, not BCD
outportb( 0x40, 255 );  // Low Timer Value
outportb( 0x40, 255 );  // High Timer Value
enable();
}


// Warning : After Midnight the cell 46c will be reset to 0 !
// this is not handled in this program
FUNCT inline u32 gettick( void )
{

disable_int();                      // Interrupt off

#if defined( BORDOS ) || defined(MDOS10) || defined(MSVC10)
pre.uw.uwh = *tick_in_dos_page_46 ;
#elif defined(DOSGNU)   // DOSGNU = DJGPP
pre.uw.uwh = _farnspeekw( 0x0000046C ) ;
#elif defined(WATCOM)
pre.uw.uwh = *(u16 *)0x0000046C ;
#endif


outportb( 0x43, 0 );    // Latch counter
pre.uc.ucl = inportb( 0x40 );
pre.uc.uch = inportb( 0x40 );
enable_int();                       // interrupt on

#if defined( BORDOS )  || defined(MDOS10) || defined(MSVC10)
post.uw.uwh = *tick_in_dos_page_46 ;
#elif defined(DOSGNU)   // DOSGNU = DJGPP
post.uw.uwh = _farnspeekw( 0x0000046C ) ;
#elif defined(WATCOM)
post.uw.uwh = *(u16 *)0x0000046C ;
#endif

if ( pre.uw.uwh != post.uw.uwh )
        {    // Der Timer-Interrupt kam dazwischen.
        post.uw.uwl = 0 ;  // Dann gehoert der Timer-Wert 0 dazu
        return post.ul.ull ;
        }
pre.uw.uwl = ~pre.uw.uwl ;
return pre.ul.ull ;
}

von m.n. (Gast)


Lesenswert?

Hans-jürgen H. schrieb:
> post.uw.uwh = _farnspeekw( 0x0000046C ) ;

Cool, genau das hat der TO gesucht!
Davor zur Sicherheit noch 2 x cli(); damit auch garnichts mehr schief 
gehen kann :-(

von Peter D. (peda)


Lesenswert?

m.n. schrieb:
> Zum einen kein Capture, zum anderen (verlangsamender) Prescaler
> aktiviert und zum 3. get_ticks() muß zyklisch aufgerufen werden, damit
> Überläufe von t2_soft wahrgenommen werden.

Stellst Du Dich absichtlich dumm?
Ob Polling oder Interrupt, ob mit oder ohne Prescaler, ob 8 oder 
16Bit-Timer spielt überhaupt keine Geige. Das Prinzip bleibt immer das 
gleiche.

von m.n. (Gast)


Lesenswert?

Das Prinzip war doch schon längst geklärt.
Die konkrete Frage des TO bezieht sich auf T1 Input Capture.
Auch zu Karneval kann daher die Kiste mit den ollen Kamellen ruhig 
geschlossen bleiben.

von S. Landolt (Gast)


Lesenswert?

> ... wie erwartet 16 Mio. Ticks gezählt ...

Bei dem gezeigten Hauptprogramm eine gewagte Aussage - wie lange müsste 
man da vor dem Controllterminal sitzen, um den seltenen Sonderfall zu 
beobachten?
  Ins Hauptprogramm gehört ein interruptgesperrter Teil mit 65536/2 
minus, sagen wir mal, zehn Prozent Takten.

von Michael K. (k-mte)


Lesenswert?

Hallo Forum,

das Programm enthält noch einen Fehler. Ich setze (in "main") zwar 
"t1_ticks" zurück, nicht aber "icr1" - was auch gar nicht möglich ist, 
da T1  ein freilaufender Zähler ist.

Lösung: "t1_ticks" nicht zurücksetzen, somit eine "absolute Zeit" 
ermitteln und die Zeitdifferenz dann durch Subtraktion des letzten 
Meßwerts vom aktuellen Wert bilden.

von Michael K. (k-mte)


Lesenswert?

Das Gefühl, ja die Gewißheit, dass in meinem Programm irgendwie der Wurm 
drin ist, führt dazu, dass es mich immer noch beschäftigt. Hier nun 
meine Überlegungen:


1. Grundlagen (in stark vereinfachter Darstellung)

Bei einem Hardware-Reset wird T1 gelöscht.

Beim Capture-Betrieb wird ein UND so vor T1 geschalten, dass T1 
hochzählt,
solange der Capture-Eingang ICP1 High ist. Bei einer negativen Flanke an
ICP1 wird der Zählerstand ins Capture-Register übernommen. Solange an 
ICP1
ein L-Pegel anliegt, zählt der Zähler nicht weiter. Wenn nach einer 
steigenden Flanke an ICP1 wieder ein H-Pegel anliegt, zählt T1 von dem 
Zählerstand aus weiter, bei dem er nach der fallenden Flanke 
stehengeblieben war.

Im folgenden wird davon ausgegangen, dass der Zählerstand des 
Capture-Registers und des Overflow-Zählers im Capture-Interrupt 
ausgelesen werden.


2. Normalbetrieb

Der Overflow-Interrupt tritt bei einem Zählerüberlauf auf (also umgefähr 
bei einem Zählerstand von 0), der Capture-Interrrupt trete bei einem 
Zählerstand von umgefähr 32.768 auf.

Beide Interrupts interferieren nicht.


3. Overflow-Interrupt im Anschluß an den Capture-Interrupt

Nun lassen wir den Capture-Interrupt immer später auftreten, schließlich 
bei einem Zählerstand von 65.535. Dies führt dazu, dass zuerst der 
höherpriore Capture-Interrupt vollständig abgearbeitet wird und im 
Anschluß daran der niederpriore Overflow-Interrupt.

Beide Interrupts interferieren nicht.


4. Capture- und Overflow-Interrupt gleichzeitig

Falls der Capture-Interrupt noch etwas später, bei einem Zählerstand von 
0
auftritt, tritt er gleichzeitig mit dem Overflow-Interrupt auf. Dies hat 
zur Folge, dass ein T1-Überlauf stattgefunden hat, ohne dass der 
Overflow-Interrupt in der Lage gewesen wäre, inzwischen den 
Overflow-Zähler hochzuzählen.

Bei der Ermittlung des Zeitpunkts ist diese Konstellation (falls 
möglich) zu detektieren und ein in der Zukunft stattfindender 
Overflow-Interrupt vorwegzunehmen. Da dieser Overflow-Interrupt den 
Overflow-Zähler nach Beendigung des Capture-Interrupts aber noch 
hochzählen wird, darf der Overflow-Zähler aber nicht wie im Beispiel von 
Peter Danegger zusätzlich im Capture-Interrupt hochgezählt werden.


5. Capture-Interrupt tritt während eines Overflow-Interrupt auf

Falls der Capture-Interrupt noch etwas später auftritt, ist inzwischen 
ein
Overflow-Interrupt eingetreten. Dabei gibt es zwei Möglichkeiten.


5.1 Overflow-Interrupt wird vor dem Hochzählen des Overflow-Zählers
    unterbrochen

Dann gilt das unter 4 gesagte.


5.2 Overflow-Interrupt wird während des Hochzählens des Overflow-Zählers
    unterbrochen

Wenn der Overflow-Interrupt unterbrochen wird, während die einzelnen 
Bytes
des Overflow-Zählers vom Typ unsigned int inkrementiert werden, hat dies 
zur Folge, dass im Capture-Interrupt beim Auslesen des Overflow-Zählers 
Mist rauskommt.


5.3 Overflow-Interrupt wird nach dem Hochzählen des Overflow-Zählers
    unterbrochen

Der im Capture-Interrupt ausgelesene Overflow-Zähler wird korrekt 
ausgelesen.


Fazit:

Vor dem Hintergrund des oben gesagten habe ich meine Zweifel, ob das von 
mir angesprochene Problem eines per Software auf 32 Bit verlängerten 
Input Captures bei einem ATmega328 überhaupt lösbar ist, geschweige denn 
technisch sauber. Insofern stehe ich dem im folgenden veröffentlichten 
Code ziemlich skeptisch gegenüber, zumal es mir mit dem mir zur 
Verfügung Stehenden bislang nicht gelungen ist, einen Problemfall zu 
generieren.
1
/** Globale Variablen **/
2
3
volatile unsigned long int t1_overflow = 0;
4
5
volatile unsigned long int temp_t1_overflow;
6
7
volatile unsigned int temp_icr1;
8
9
volatile unsigned char problem;
10
11
volatile unsigned char flag = 0;
12
13
/** Overflow-Interrupt **/
14
15
ISR (TIMER1_OVF_vect)
16
  {
17
  t1_overflow++;
18
  }
19
20
/** Capture-Interrupt **/
21
22
ISR (TIMER1_CAPT_vect)
23
  {
24
  unsigned char sreg = SREG;
25
26
  cli ();
27
28
  if ((TIFR1 & (1<<TOV1)) && (ICR1 < 0x8000))
29
      problem = 1;
30
    else  problem = 0;
31
32
  temp_t1_overflow = t1_overflow;
33
34
  temp_icr1 = ICR1;
35
36
  flag = 1;
37
38
  SREG = sreg;
39
  }
40
41
/** Hauptprogramm **/
42
43
void capture (void)
44
  {
45
  unsigned long int neuer_wert, alter_wert = 0;
46
47
  uart_init ();
48
49
  t1_init ();
50
51
  while (1)
52
    {
53
    while (flag == 0)
54
      ;
55
56
    neuer_wert = ((unsigned long) temp_icr1) + (temp_t1_overflow << 16);
57
58
    if (problem == 1)
59
        {
60
        neuer_wert += (unsigned long) 0x10000;
61
62
        printf ("* ");
63
        }
64
      else  {
65
        printf ("  ");
66
        }
67
68
    printf ("%10li\r\n", (unsigned long int) neuer_wert - (unsigned long int) alter_wert);
69
70
    alter_wert = neuer_wert;
71
72
    flag = 0;
73
    }
74
  }

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Michael K. schrieb:
> Beim Capture-Betrieb wird ein UND so vor T1 geschalten, dass T1
> hochzählt,
> solange der Capture-Eingang ICP1 High ist. Bei einer negativen Flanke an
> ICP1 wird der Zählerstand ins Capture-Register übernommen. Solange an
> ICP1
> ein L-Pegel anliegt, zählt der Zähler nicht weiter. Wenn nach einer
> steigenden Flanke an ICP1 wieder ein H-Pegel anliegt, zählt T1 von dem
> Zählerstand aus weiter, bei dem er nach der fallenden Flanke
> stehengeblieben war.

Davon ist das allermeiste falsch. RTFM.

Oliver

von Peter D. (peda)


Lesenswert?

Michael K. schrieb:
> Beim Capture-Betrieb wird ein UND so vor T1 geschalten, dass T1
> hochzählt,
> solange der Capture-Eingang ICP1 High ist.

Wozu soll das gut sein?
Was willst Du damit erreichen?

von Rainer V. (a_zip)


Lesenswert?

Michael K. schrieb:
> Vor dem Hintergrund des oben gesagten habe ich meine Zweifel, ob das von
> mir angesprochene Problem eines per Software auf 32 Bit verlängerten
> Input Captures bei einem ATmega328 überhaupt lösbar ist, geschweige denn
> technisch sauber. Insofern stehe ich dem im folgenden veröffentlichten
> Code ziemlich skeptisch gegenüber, zumal es mir mit dem mir zur
> Verfügung Stehenden bislang nicht gelungen ist, einen Problemfall zu
> generieren.

Also bitte, du hast doch alles bekommen, was du brauchst. Was willste 
denn an dem simplen Konstrukt jetzt überhaupt noch austesten?? Und dann 
auch noch technisch sauber...manohman. Es drängt sich mir der Verdacht 
auf, dass du einfach nicht verstanden hast, was das Beispielprogramm 
macht und dann ist es natürlich nicht so einfach, einen "Problemfall" zu 
generieren :-)
Gruß Rainer

von Michael K. (k-mte)


Angehängte Dateien:

Lesenswert?

Hallo Forum,

anbei habe ich die letzte Version meines Programms hochgeladen.

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.