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!
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.
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.
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.
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.
Ok, danke, ihr habt mir auf jeden fall schonmal ein paar wichtige Denkimpulse geliefert!
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.
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.
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.
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.
>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.
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.
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".
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ß...
Zeig doch mal deine Interrupt-Service-Routine und schraub die verfügbare Frequenz auf 8 MHz.
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 |
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.
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.
Außerdem hat der Tiny48 im unteren Bereich 3 GP-Register die man sicher nicht nur für Stacksichern einsetzen könnte.
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 |
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.
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.
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.
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
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.
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.
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. :)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.