Forum: Mikrocontroller und Digitale Elektronik ca. 40 - 60 kHz mit "Pin Change Interrupt" (ATtiny48) zählen?!


von EGS_TI (Gast)


Lesenswert?

Hallo!

Gäbe es etwas bestimmtes zu beachten, wenn man eine Frequenz von ca. 
40-60 kHz mit Hilfe eines PCINT vom ATtiny48 zählen möchte?
Normalerweise sollte der Interrupt ja bei der fallenden und der 
steigenden Flanke ausgelöst werden, wenn ich das Datenblatt richtig 
gelesen habe.

Meine ersten Versuche waren leider nicht wirklich von Erfolg gekrönt.
Also er hat nicht richtig gezählt.

Mein Eingang ist direkt mit dem Ausgang eines anderen Microcontrollers 
verbunden.
Zusätzlich habe ich einen Pulldown mit 10k vor meinen Eingang gesetzt.

Wäre es vielleicht besser, den Counter eines Timers für diese Aufgabe zu 
verwenden?


Muchas gracias!

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Man könnte auch den Abstand zwischen zwei Pulsen mit einem Timer messen.
......16 Bit-Timmer vornehmlich.

60 kHz lassen bei 16 MHz kaum etwas mehr als 100 Takt Platz für die ISR, 
da es ja ein Peak von 120 kHz gibt.

Wäre ein EXT-Interrupt nicht die mittelbeste Wahl ,wenns denn schon über 
Interrupt gezählt werden soll, da die Flankenbedingung wählbar ist für 
Auslösung eines Interruptes ?

Wenn man mit Timer zählt kann man zwei Flankenabstände messen.

von Ulrich (Gast)


Lesenswert?

Am sich sollte das Zählen per Pinchange schon gehen. Je nach Takt des µC 
sind 50 kHz schon recht schnell und brauchen einen einigermaßen 
schnellen Code.
Bei 1 MHz Takt hätte man nur 9-10 Zyklen für die ISR. Das wird auch 
schon in ASM sehr knapp, falls es überhaupt reicht. Die meisten Compiler 
werden einen deutlich höheren Takt brauchen, weil meist einiges An 
overhead für die ISR dazu kommt.

Der externe Zähleingang eines Timers ist auf alle Fälle einfacher und 
schneller.

von Heiko B. (heiko_b)


Lesenswert?

Bei 60000 bzw 120000 Interrupts/Sekunde hast du nur ca. 8 CPU-Befehle 
pro MHz Taktfrequenz zwischen deinen Interrupts. Da must du schon 
ordentlich optimieren, um da noch ein sinnvolles Programm 
zwischenzuquetschen. Bei der vagen Beschreibung deines Projektes wirst 
du nicht auf viel Hilfe hoffen dürfen. Aber die Verwendung eines 
Counters klingt schon mal besser.

von EGS_TI (Gast)


Lesenswert?

Dennis Heynlein schrieb:
> 60 kHz lassen bei 16 MHz kaum etwas mehr als 100 Takt Platz für die ISR,
> da es ja ein Peak von 120 kHz gibt.

Das wäre nicht schlimm.

Momentan läuft mein Controller noch mit den Standardeinstellungen (8 
MHz, CLKDIV8), also mit 1 MHz.

Also mein konkretes Problem ist, dass er scheinbar nicht richtig zählt.
Ist die Frequenz möglicherweise zu hoch?

Ich bekomme nur unter bestimmten Bedingungen diesen Takt, von daher 
würden die Interrupts nur vereinzelt auftreten und dann soll auch nur 
ein Portpin gesetzt bzw. gelöscht werden. In der ISR wäre also nicht so 
viel zu tun.

von EGS_TI (Gast)


Lesenswert?

Ok, danke, ihr habt mir auf jeden fall schonmal ein paar wichtige 
Denkimpulse geliefert!

von EGS_TI (Gast)


Lesenswert?

EGS_TI schrieb:
> Ich bekomme nur unter bestimmten Bedingungen diesen Takt, von daher
> würden die Interrupts nur vereinzelt auftreten und dann soll auch nur
> ein Portpin gesetzt bzw. gelöscht werden. In der ISR wäre also nicht so
> viel zu tun.

Also in der ISR soll, wenn der x-te Impuls festgestellt wurde, ein Pin 
gesetzt oder gelöscht werden.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Heiko Bendt schrieb:
> Bei 60000 bzw 120000 Interrupts/Sekunde hast du nur ca. 8 CPU-Befehle
> pro MHz Taktfrequenz zwischen deinen Interrupts. Da must du schon
> ordentlich optimieren, um da noch ein sinnvolles Programm
> zwischenzuquetschen.

8 Zyklen reichen definitiv nicht fürs Rein in die ISR und wieder 
Zurückspringen. Schau am besten mal ins Datenblatt. Ich bin mir nicht 
mehr sicher, aber wahrscheinlich verbrät der Verwaltungsaufwand alleine 
schon 12 bis 14 Zyklen. Dazu kommen noch ein paar weitere Zyklen, denn 
du willst ja irgendwas zählen. Außerdem musst du das Statusregister 
sichern und wieder zurückspeichern.

Ich hoffe, du programmierst das in Assembler und nicht in C oder gar 
Bascom. :-)

Ein Tipp: Nimm nicht den Pin-Change-Interrupt, sondern löse nur mit 
einer der Flanken einen Interrupt aus (z.B. mit der steigenden), dann 
hast du nur halb so viele Interrupts. INT0 und INT1 können das beim 
ATtiny48.

von Ulrich (Gast)


Lesenswert?

Mit 1 MHz Takt sind das nur rund 9 oder 10 Zyklen. Das ist einfach zu 
schnell um da noch per ISR zu zählen.

Wenn man den µC mit 8 MHz laufen läßt hat man eine Chance, wenn das 
Programm in Ordnung ist, und der Compiler nicht besonders langsam. Wenn 
man nicht aufpasst reicht es immer noch nicht.

Bei 1 MHz Takt liegt die obere Grenze für das Zählen im Interrupt eher 
so bei 5-20 kHz, je nach Compiler und Programm.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

P.S.: Weiß grad nicht, ob das beim ATtiny48 geht, aber bei manchen AVRs 
kann man für einen der Timer eine externe Taktquelle angeben. Dadurch 
lässt du die Hardware für dich zählen und sparst den Interrupt ganz. 
Notfalls halt auf einen anderen AVR umsteigen.

von holger (Gast)


Lesenswert?

>Also mein konkretes Problem ist, dass er scheinbar nicht richtig zählt.
>Ist die Frequenz möglicherweise zu hoch?

Nein, die Frequenz ist nicht zu hoch.
Dein beschissener uC ist zu langsam.

von EGS_TI (Gast)


Lesenswert?

Markus W. schrieb:
> Ich hoffe, du programmierst das in Assembler und nicht in C

In C ;)

Markus W. schrieb:
> INT0 und INT1 können das beim
> ATtiny48.

Das wäre auch eine Alternative. Nur muss ich erstmal checken, ob die 
noch frei sind. Ansonsten könnte ich das sicherlich auch noch ändern.

Ich denke, als nächstes probiere ich es glaube ich mal mit dem 
Timer/Counter.

von EGS_TI (Gast)


Lesenswert?

holger schrieb:
> Dein beschissener uC ist zu langsam.

Stimmt! xD

von EGS_TI (Gast)


Lesenswert?

Ulrich schrieb:
> Mit 1 MHz Takt sind das nur rund 9 oder 10 Zyklen. Das ist einfach zu
> schnell um da noch per ISR zu zählen.

Darüber habe ich mir garkeine Gedanken gemacht. schäm

Das erklärt ja dann schonmal auf jeden Fall das "Fehlverhalten".

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

EGS_TI schrieb:
> Markus W. schrieb:
>> Ich hoffe, du programmierst das in Assembler und nicht in C
>
> In C ;)

Echt mutig! :-)
Schau dir mal an, was dein Compiler aus der ISR macht und prüfe in der 
AVR-Doku, wie viele Zyklen dann verbraten werden.

Kleine AVR – und da gehört für mich auch der ATtiny48 dazu – programmier 
ich lieber in Assembler. Dann gehen nämlich so Tricks, dass du für 
Hauptprogramm und ISR unterschiedliche Registergruppen verwendest und 
beim Sprung in die ISR gar nichts sichern musst. Außer dem 
Status-Register, das du dann aber nicht auf den Stack zu schieben 
brauchst, sondern auch in einem eigenen Register sicherst. All das spart 
viel Rechenzeit.

Klar, ein guter Compiler würde diese Optimierung auch machen können. 
Aber dann muss es schon unglaublich gut sein. So unglaublich, dass ich 
nicht recht glauben kann, dass es ihn gibt. ;-) Aber wer weiß...

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Zeig doch mal deine Interrupt-Service-Routine und schraub die verfügbare 
Frequenz auf 8 MHz.

von EGS_TI (Gast)


Lesenswert?

Dennis Heynlein schrieb:
>  schraub die verfügbare Frequenz auf 8 MHz.

Das muss ich auch nochmal ausprobieren.
1
00000030  PUSH R1    Push register on stack 
2
00000031  PUSH R0    Push register on stack 
3
00000032  IN R0,0x3F    In from I/O location 
4
00000033  PUSH R0    Push register on stack 
5
00000034  CLR R1    Clear Register 
6
00000035  PUSH R24    Push register on stack 
7
  if (CLK_Flanken_Zaehler == 2)
8
00000036  LDS R24,0x0101    Load direct from data space 
9
00000038  CPI R24,0x02    Compare with immediate 
10
00000039  BRNE PC+0x02    Branch if not equal 
11
    DataHigh();
12
0000003A  SBI 0x05,4    Set bit in I/O register 
13
  ++CLK_Flanken_Zaehler;
14
0000003B  LDS R24,0x0101    Load direct from data space 
15
0000003D  SUBI R24,0xFF    Subtract immediate 
16
0000003E  STS 0x0101,R24    Store direct to data space

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

8 MHz zu 120 kHz würde knappe 66 Takte bedeuten.

jeder PUSH/POP-Befehl frist schonmal 2 Takte.

Welche Zeitbasis benutzt Du ?

Eine Timer(-interrupt) oder etwa _delay_us in der main ?

Wie greifst du auf die Variable in der ISR zu ?

mit einem CLI(); temp=var; SEI() Konstrukt ?

Was ist "if (CLK_Flanken_Zaehler == 2)" ?

Was ist DataHigh() ? So ein Funktionsaufruf verschlingt locker 8 Takte 
für Rücksprungadresse speicher/zurückholen und Prelog/Postlog.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

EGS_TI schrieb:
1
> 00000030  PUSH R1    Push register on stack
2
> 00000031  PUSH R0    Push register on stack
3
> 00000032  IN R0,0x3F    In from I/O location
4
> 00000033  PUSH R0    Push register on stack
5
> 00000034  CLR R1    Clear Register
6
> 00000035  PUSH R24    Push register on stack
7
>   if (CLK_Flanken_Zaehler == 2)
8
> 00000036  LDS R24,0x0101    Load direct from data space
9
> 00000038  CPI R24,0x02    Compare with immediate
10
> 00000039  BRNE PC+0x02    Branch if not equal
11
>     DataHigh();
12
> 0000003A  SBI 0x05,4    Set bit in I/O register
13
>   ++CLK_Flanken_Zaehler;
14
> 0000003B  LDS R24,0x0101    Load direct from data space
15
> 0000003D  SUBI R24,0xFF    Subtract immediate
16
> 0000003E  STS 0x0101,R24    Store direct to data space

Uff... Hast du beim Compiler die Optimierungen aktiviert?
In Assembler würde die ISR weniger als halb so lange brauchen. Aber wenn 
du den Takt hoch genug schraubst, sollte es auch mit C sicher klappen.

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Außerdem hat der Tiny48 im unteren Bereich 3 GP-Register die man sicher 
nicht nur für Stacksichern einsetzen könnte.

von EGS_TI (Gast)


Lesenswert?

EGS_TI schrieb:
> DataHigh();
> 0000003A  SBI 0x05,4    Set bit in I/O register

DataHigh() ist nur ein Makro, also dieses "SBI":
#define DataHigh() PORTB |= (1<<PORTB4)

Der Code oben ist das Disassembly von meinem C-Code der ISR.
Einen Teil hatte ich vergessen zu kopieren.
Der komplette Code steht jetzt nochmal unten.

Da ich in C programmiere ist dort auch die entsprechende C-Code-Zeile 
eingebettet, jeweils vor dem jeweiligen Assemblercode.

Aus meinem C-Code:
1
ISR(PCINT0_vect)
2
{
3
  
4
  if (CLK_Flanken_Zaehler == 2)
5
  {
6
    DataHigh();
7
  }
8
9
  ++CLK_Flanken_Zaehler;
10
}
1
00000030  PUSH R1    Push register on stack 
2
00000031  PUSH R0    Push register on stack 
3
00000032  IN R0,0x3F    In from I/O location 
4
00000033  PUSH R0    Push register on stack 
5
00000034  CLR R1    Clear Register 
6
00000035  PUSH R24    Push register on stack 
7
  if (CLK_Flanken_Zaehler == 2)
8
00000036  LDS R24,0x0101    Load direct from data space 
9
00000038  CPI R24,0x02    Compare with immediate 
10
00000039  BRNE PC+0x02    Branch if not equal 
11
    DataHigh();
12
0000003A  SBI 0x05,4    Set bit in I/O register 
13
  ++CLK_Flanken_Zaehler;
14
0000003B  LDS R24,0x0101    Load direct from data space 
15
0000003D  SUBI R24,0xFF    Subtract immediate 
16
0000003E  STS 0x0101,R24    Store direct to data space 
17
}
18
00000040  POP R24    Pop register from stack 
19
00000041  POP R0    Pop register from stack 
20
00000042  OUT 0x3F,R0    Out to I/O location 
21
00000043  POP R0    Pop register from stack 
22
00000044  POP R1    Pop register from stack 
23
00000045  RETI     Interrupt return

von Thomas E. (thomase)


Lesenswert?

Liegt dir dieser verschissene Popelcontroller, also ich meine diese 
echte Innovation, wirklich so am Herzen, daß du den nicht durch einen 
einen Atmega48 ersetzen magst? Der kann im Gegensatz zu dem Tiny nämlich 
20MHz mit einem Quarz, hat auch noch einen Timer mehr und kostet ein 
paar Cent weniger.

Dennis Heynlein schrieb:
> 60 kHz lassen bei 16 MHz
Die Gurke kann nur 12.

mfg.

von EGS_TI (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Der kann im Gegensatz zu dem Tiny nämlich
> 20MHz mit einem Quarz, hat auch noch einen Timer mehr und kostet ein
> paar Cent weniger.

Die Entscheidung hing eigentlich nur davon ab, dass er vor Ort verfügbar 
war, günstig ist und ausreichend I/O-Pins besitzt.
20 MHz brauchen wir garnicht für die popligen Sachen, die der nur machen 
soll. Timer brauchen wir auch nicht wirklich. TWI sollte er noch haben, 
mehr eigentlich nicht.
Ursprünglich wollte ich den Takt auch noch mit dem SPI-Modul 
"auswerten", wobei sich das als nicht möglich herausgestellt hat.

von H.Joachim S. (crazyhorse)


Lesenswert?

Frequenzmessung mit pinchange ist grundsätzlich schon erst mal Mist, 
zumindest, wenn es auch die Möglichkeit der flankentriggerung gibt.
Ausserdem handelt man sich sinnlos Probleme ein, wenn das Signal stark 
unsymmetrisch ist.

von Peter D. (peda)


Lesenswert?

EGS_TI schrieb:
> Also in der ISR soll, wenn der x-te Impuls festgestellt wurde, ein Pin
> gesetzt oder gelöscht werden.

Das geht ganz ohne Interrupt:
Den Timer auf externen Takt setzen und einen Output-Compare-Pin als 
Ausgang. Dann nur noch das Compareregister auf die gewünschte Anzahl 
Impulse setzen und die Hardware machen lassen.


Peter

von David (Gast)


Lesenswert?

Du kannst mit JK-FlipFlops die Frequenz halbieren, vierteln, achteln 
usw...
Je nach dem wieviele Flipflops du verwendest. Dadurch hast du dann mehr 
zeit für die ISR.

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Im Prinzip ist es ja nun möglich.
- Takt-Frequenz auf Maximum (12 MHz).
- Optimum wäre natürlich eine in Assembler implementierte ISR (mit
  Ausnutzung der General-Purpose-Register für Impulszähler und
  SREG-Backup).
- ein EXT-Interrupt wäre wohl für die Lastreduzierung durch die ISR auch
  nicht die schlechteste Wahl. (Nur bei Rising oder Falling Edge 
auslösen)
- ein Timer für die Zeitbasis wäre wohl Pflicht.
und es spart wohl zusätzliche externe Beschaltungen durch FlipFlops etc.

von EGS_TI (Gast)


Lesenswert?

Hallo, vielen herzlichen Dank für die ganzen Antworten!

Ich habe heute mein Problemchen dank euch in den Griff bekommen. Ich 
weiß nicht, ob ich von alleine darauf gekommen wäre. Bin ganz schön 
froh, dass es euch gibt!

Ich habe mich vorerst dafür entschieden, bei der Variante mit dem PCINT 
zu bleiben.
Ich habe den tiny anfangs ja mit dem internen standardmäßig 
eingestelltem Takt von 8 MHz und dem CKDIV8-Fuse betrieben.

Wie hier ja weiter oben schon geschrieben wurde, war dadurch die 
Abarbeitung der ISR zu langsam.
Dadurch konnten einige der Taktflanken des an dem PCINT-Pin 
angeschlossenen Taktes keinen Interrupt auslösen.
Nachdem ich die CKDIV8-Fuse gelöscht hatte, funktionierte mein Code dann 
tadellos. Alles nach wie vor in C. :)

von EGS_TI (Gast)


Lesenswert?

Peter Dannegger schrieb:
> EGS_TI schrieb:
>> Also in der ISR soll, wenn der x-te Impuls festgestellt wurde, ein Pin
>> gesetzt oder gelöscht werden.
>
> Das geht ganz ohne Interrupt:
> Den Timer auf externen Takt setzen und einen Output-Compare-Pin als
> Ausgang. Dann nur noch das Compareregister auf die gewünschte Anzahl
> Impulse setzen und die Hardware machen lassen.
>
>
> Peter

Das wäre natürlich noch die Sauberste Lösung, wird womöglich am Ende 
darauf hinauslaufen!

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.