Forum: Mikrocontroller und Digitale Elektronik ATtiny85 - externer Interrupt


von Matthias H. (mhage)


Lesenswert?

Hallo,

Ich habe am tiny85 (8MHz intern, INT0) einen Frequenzgenerator 
angeschlossen, der auf 15Hz eingestellt ist. Ich möchte nun die Zeit in 
Mikrosekunden messen, die zwischen zwei Interrupts vergeht. Dabei wird 
nur die fallende Flanke ausgewertet. Hier meine Konfiguration:
1
   MCUCR |= (1 << ISC01); // The falling edge of INT0 generates an interrupt request.
2
   GIMSK |= (1 << INT0);  // External Interrupt Request 0 Enable
3
4
ISR(INT0_vect) {
5
   triggered = true;
6
}
7
8
9
loop() {
10
    if(triggered) {
11
    
12
    newTriggerTime = micros();
13
    triggered = false;
14
 
15
    uint32_t us;
16
    if ( newTriggerTime > oldTriggerTime ) {
17
      us = newTriggerTime - oldTriggerTime;
18
    } else {
19
      us = (4294967295UL - oldTriggerTime + newTriggerTime + 1); 
20
    }
21
    Serial.print(F("Difference : "));
22
    Serial.println(us);
23
        
24
    oldTriggerTime = newTriggerTime;
25
    
26
  }
27
}

Was dabei herauskommt ist nicht, wie ich erwartet hätte ca. 66000 us. 
Sondern wechselnde Werte unter 2000 us. Was mich glauben lässt, dass es 
mehr Interrupts gibt als es eigentlich sein dürften. Wie kann das sein?

Ich hoffe, jemand kann helfen...

VG Matze

von Falk B. (falk)


Lesenswert?

Matthias H. schrieb:
> Ich habe am tiny85 (8MHz intern, INT0) einen Frequenzgenerator
> angeschlossen, der auf 15Hz eingestellt ist. Ich möchte nun die Zeit in
> Mikrosekunden messen, die zwischen zwei Interrupts vergeht. Dabei wird
> nur die fallende Flanke ausgewertet. Hier meine Konfiguration:

Es gibt keine Konfiguration, wohl aber einen Quelltext. Den sollte mn 
VOLLSTÄNDIG bereitstellen. Einfach als Datei anhängen, ist gar nicht 
schwer.

> loop() {
>     if(triggered) {
>
>     newTriggerTime = micros();
>     triggered = false;
>
>     uint32_t us;
>     if ( newTriggerTime > oldTriggerTime ) {
>       us = newTriggerTime - oldTriggerTime;
>     } else {
>       us = (4294967295UL - oldTriggerTime + newTriggerTime + 1);
>     }

Diesen Unsinn braucht man nicht. Wenn die Differenz kleiner als der 
halbe Zählerumfang ist, ist die Differenz immer korrekt, auch beim 
Überlauf.
1
  us = newTriggerTime - oldTriggerTime;

> Was dabei herauskommt ist nicht, wie ich erwartet hätte ca. 66000 us.
> Sondern wechselnde Werte unter 2000 us. Was mich glauben lässt, dass es
> mehr Interrupts gibt als es eigentlich sein dürften. Wie kann das sein?

Dein triggered ist hoffentlich als volatile deklariert, siehe 
Interrupt.

: Bearbeitet durch User
von Ralf K. (kurtx)


Lesenswert?

Hallo Matthias,

woher nimmst du denn die new / old Triggertime?
Der Tiny85 hat nur Timer mit 8 Bit (0..255)

Sowas macht man mit Input-Capture, wofür es einen Tiny24/44/84 mit
16-Bit-Timer braucht. Eventuell mit einem Zusatzregister, dass die 
Überläufe erfasst.

von Falk B. (falk)


Lesenswert?

Ralf K. schrieb:
> woher nimmst du denn die new / old Triggertime?

Glozen ufff!!

newTriggerTime = micros();

von Ralf K. (kurtx)


Lesenswert?

Reg dich mal wieder ab, Falk!

Bei nem popligen Tiny85 nehm ich keine Hochsprache.

Wie ist micros() definiert - Bitweite?
Woraus gewinnt micros() den Wert?

von Falk B. (falk)


Lesenswert?

Ralf K. schrieb:
> Reg dich mal wieder ab, Falk!
>
> Bei nem popligen Tiny85 nehm ich keine Hochsprache.

der OP aber schon . . .

> Wie ist micros() definiert - Bitweite?

https://docs.arduino.cc/language-reference/en/functions/time/micros/

> Woraus gewinnt micros() den Wert?

Aus einem Timer. Natürlich hat der nicht bei JEDEM Arduino die echte, 
volle Auflösung.

Beitrag #7688035 wurde vom Autor gelöscht.
von Matthias H. (mhage)


Lesenswert?

Falk B. schrieb:
> Dein triggered ist hoffentlich als volatile deklariert, siehe

Ja, natürlich. Viel mehr Code als das, was ich gepostet habe, gibt es 
gar nicht.
Ist ein mini Testprogramm, welches zunächst erstmal nur diese eine 
Aufgabe erfüllen soll.

Im setup findet man noch diese drei Zeilen:
1
CLKPR |= (1 << CLKPCE);                                                       // Aktivieren der Taktprescaler-Änderung
2
  CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);        // Setzen auf 8 MHz ohne Prescaler
3
  OSCCAL = 0x89;

mehr ist es nicht...

Was die Wahl des Controllers angeht, das ist leider durch die Hardware 
vorgegeben. Um den tiny85 komme ich nicht herum. Da ich normalerweise 
auf andere Typen setze und dies meine erste Berührung mit dem Tiny85 
ist, kenne ich natürlich auch seine Eigenarten nicht. Ist also gut 
möglich, dass ich das völlig falsch angehe und/oder wesentliches 
vergessen habe.

von Spess53 .. (hardygroeger)


Lesenswert?

Hi

>und dies meine erste Berührung mit dem Tiny85
>ist, kenne ich natürlich auch seine Eigenarten nicht.

Faule Ausrede. Wozu gibt es Datenblätter. Und sogar noch von Atmel!

MfG Spess

von Matthias H. (mhage)


Lesenswert?

Du wirst lachen, aber ich habe tatsächlich das Datenblatt zu Rate 
gezogen. Dennoch funktioniert es nicht.

Aber vielen Dank für Deinen hilfreichen Beitrag.

von Falk B. (falk)


Lesenswert?

Matthias H. schrieb:
> Ja, natürlich. Viel mehr Code als das, was ich gepostet habe, gibt es
> gar nicht.

Umso einfacher ist es, den Quelltext VOLLSTÄNDIG und im ORIGINAL hier 
beizustellen! Du wärst nicht der Erste, der beim Kopieren oder gar 
ABSCHREIBEN einen Fehler einbaut.

von Schwierig (ruelps)


Lesenswert?

> mehr ist es nicht...

Ach so. Und wo sind triggered usw. deklariert?

von N. M. (mani)


Lesenswert?

Man sieht auch nirgends ein sei().

Die Fuses sind bestimmt auch noch interessant. Die also auch gleich noch 
auslesen und posten.
Ist die Frage ob es gut ist irgendwie am Takt zu drehen wenn deine 
Arduino Lib von etwas anderem ausgeht.
Keine Ahnung ob man der das im Nachhinein mitteilen kann oder ob das 
Fest ist.
Wenn das nicht der Defaultwert ist, ist mir klar warum keine Zeiten mehr 
passen.
Also evtl Mal alles zurück auf Anfang und NICHT am Takt drehen. 
Vielleicht geht's dann. Wäre ein einfacher Test.

von Peter D. (peda)


Lesenswert?

Matthias H. schrieb:
> Ich hoffe, jemand kann helfen...

Tippe auf "Durchsuchen" und wähle das vollständige .ino aus (ziehen 
geht auch).

: Bearbeitet durch User
von Matthias H. (mhage)


Angehängte Dateien:

Lesenswert?

Ihr habt ja Recht....

Im Anhang findet Ihr das Testprojekt. Die Fuses sind wie folgt:

lfuse = 0xe2
hfuse = 0xdf
efuse = 0xff
lock = 0xff

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Zuerst würde ich mal einen 16-Bit Timer aufsetzen. Das machst du mit dem 
Timer Overflow Interrupt, der das obere Byte hochzählt. Zusammengesetzt 
mit dem CNT des Timers hast du dann einen 16 Bit Timer. Das kannste 
kaskadieren und noch ein Byte hochzählen, wenn du Werte mit mehr als 
65535 µs erwartest.

Im flankengesteuerten Interrupt liest du alle 3 Bytes (High, Mid, CNT) 
aus und speicherst sie an einen Safeplace. Alles löschen und den Timer 
wieder mit 1µs Zykluszeit starten (Prescaler 8).
Damit ist der Arduino Timer raus und du bekommst ganz gute Werte.
Das Ergebnis ergibt sich aus (65536 x High) + (256 x Mid) + CNT.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Der Code sieht prinzipiell o.k. aus.
Es könnte sein, daß Dir die Dauer der seriellen Ausgabe in die Suppe 
spuckt.
Ich würde daher die Zeitermittlung direkt im Interrupt machen.
Dabei nicht vergessen, die Übernahme des Wertes in der Loop atomar zu 
kapseln.
Dein Generator liefert 5V Rechteck?

: Bearbeitet durch User
von Mario M. (thelonging)


Lesenswert?

Matthias H. schrieb:
> Was dabei herauskommt ist nicht, wie ich erwartet hätte ca. 66000 us.

Dein ssPrintln kann nur 16 Bits.

von Mi N. (msx)


Lesenswert?

Matthias S. schrieb:
> Zuerst würde ich mal einen 16-Bit Timer aufsetzen. Das machst du
> mit dem
> Timer Overflow Interrupt, der das obere Byte hochzählt. Zusammengesetzt
> mit dem CNT des Timers hast du dann einen 16 Bit Timer. Das kannste
> kaskadieren und noch ein Byte hochzählen, wenn du Werte mit mehr als
> 65535 µs erwartest.

Es reicht, die Überläufe mit 32 Bit zu zählen,
1
ISR (TIMER1_OVF_vect)
2
{
3
  ueberlauf++;                           // grobe Zeitmessung mit T1
4
}
und den Wert des 8 Bit Timers dazuzupacken:
1
temp_ueberlauf = ueberlauf;              // ueberlauf++ per ISR
2
if(TIFR & BIT(TOV1) && (TCNT1 < 0x80))   // noch einer offen?
3
  temp_ueberlauf++;                      // dann +1
4
zeitpunkt = temp_ueberlauf * 0x100 + TCNT1;

Mario M. schrieb:
> Dein ssPrintln kann nur 16 Bits.

Dann vielleicht in zwei Hälften ausgeben.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Mi N. schrieb:
> Dann vielleicht in zwei Hälften ausgeben.

Der ATTiny85 hat genug Flash für sprintf.
1
#include <stdio.h>
2
  char buffer[20];
3
  sprintf( buffer, "Difference : %lu\n", us );
4
  Serial.print( buffer );

von Mi N. (msx)


Lesenswert?

Peter D. schrieb:
> Der ATTiny85 hat genug Flash für sprintf.

Sag das den TO! Er verwendet ssPrintln.

von Matthias H. (mhage)


Lesenswert?

Mi N. schrieb:
> Matthias S. schrieb:
>
> Es reicht, die Überläufe mit 32 Bit zu zählen,ISR (TIMER1_OVF_vect)
> {
>   ueberlauf++;                           // grobe Zeitmessung mit T1
> }
> und den Wert des 8 Bit Timers dazuzupacken:temp_ueberlauf = ueberlauf;
> // ueberlauf++ per ISR
> if(TIFR & BIT(TOV1) && (TCNT1 < 0x80))   // noch einer offen?
>   temp_ueberlauf++;                      // dann +1
> zeitpunkt = temp_ueberlauf * 0x100 + TCNT1;

Habe das mal so umgesetzt. Habe das Ergebnis erstmal geteilt und 
ausgegeben.

32
21
-1
-23
-24
84
-54
51
-23
-23
78
-28
-16

Peter D. schrieb:
> Dein Generator liefert 5V Rechteck?

Ja, der liefert ein Rechteck. HighLevel 5,0V ; LowLevel 0mV ; 15Hz

von Falk B. (falk)


Lesenswert?

Ich sehe da mehrere Probleme.

#define SENSOR    PB2     // pin7
#define LED      PB4     // pin3

  pinMode (SENSOR, INPUT);
  pinMode (LED, OUTPUT);

Das geht so nicht. Die digitalen Ios sind in der Arduino-Welt mit 
Nummern versehen, also 1, 2,3,4. PB2 und PB4 sind Namen der "normalen" 
avr gcc Welt. Die funktionieren nicht mit der Funktion pinMode() bzw. 
man schaltet die falschen IOs.

Wenn deine komische ssPrintln() Funtkion nur 16 Bit ausgeben kann, 
probier's mal so.

ssPrintln(us/10);

Ein sei() wird bei Arduino nicht benötigt, denn in der setup-Funktion 
sind die Interrupts schon aktiv, das macht die Arduino-Software vorher. 
Sie schadet aber auch nicht.

von Falk B. (falk)


Lesenswert?

Probier's mal so.
1
#include "SS_Tiny85.h"
2
3
#define SENSOR    PB2     // pin7   
4
#define LED      PB4     // pin3 
5
6
volatile bool triggered = false;
7
uint32_t oldTriggerTime = 0;       
8
uint32_t newTriggerTime = 0;       
9
10
ISR(INT0_vect) {
11
  
12
  triggered = true;
13
  
14
}
15
16
void setup() {
17
  CLKPR |= (1 << CLKPCE);                                                       // Aktivieren der Taktprescaler-Änderung
18
  CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);        // Setzen auf 8 MHz ohne Prescaler
19
  OSCCAL = 0x89;
20
  
21
  pinMode (SENSOR, INPUT);
22
  pinMode (LED, OUTPUT);
23
24
  MCUCR |= (1 << ISC01);                  // The falling edge of INT0 generates an interrupt request.
25
  GIMSK |= (1 << INT0);                   // External Interrupt Request 0 Enable
26
27
  ssBegin(9600);
28
}
29
30
void loop() {
31
  if ( triggered ) {
32
33
    triggered = false;    
34
    newTriggerTime = micros();    
35
    uint32_t us;
36
    us = newTriggerTime - oldTriggerTime;
37
    ssPrint(F("difference : "));
38
    ssPrintln(us/10);    
39
    oldTriggerTime = newTriggerTime;
40
  } 
41
}

von Mi N. (msx)


Lesenswert?

Matthias H. schrieb:
> Habe das mal so umgesetzt. Habe das Ergebnis erstmal geteilt und
> ausgegeben.
>
> 32
> 21
> -1
> -23

Kleine Werte sind es ja, aber was Du da gemacht hast, sehe ich nicht.

Es ist vielleicht etwas der Holzhammer, aber die Tage hatte ich hier das 
vollständige Programm gezeigt und nicht nur die obigen Schnipsel:
Beitrag "Re: Analoges Fahrrad-Tachymeter mit Dynamo betreiben"

Da wird alles in den Interruptroutinen erledigt und ein PCINT-Pin 
verwendet. Die Auswertung in loop() ist immer eine schlechte Idee!
Wenn Du die Endung auf .ino änderst, sollte Arduino das auch Übersetzen 
können. Selbst habe ich es nicht probiert.

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.