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.
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ß...
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.
> 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.
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
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!