Hallo zusammen Ich bin xmega Neuling (und auch neu im Forum :-)) und komme bei einem Projekt nicht mehr weiter. Ich möchte ein Eingangssignal welches an einem Einganspin anliegt teilen und das geteilte Signal an einem Ausgangspin wider ausgeben. Der Teilungsfaktor soll per USART gewählt werden können. Grundsätzlich funktioniert mein Code wunderbar, aber eben leider nur sehr langsam (bis fin ca. 178KHz, danach kommt der Controller nicht mehr nach). Virtual Ports habe ich bereits versucht, dass hat nicht mehr Geschwindigkeit gebracht. Ich verstehe einfach nicht wieso er uC nicht nachkommt, bei 32MHz sollte das doch kein Problem sein oder?. Die 32MHz habe ich nachgemessen. Hat jemand eine Idee? //Globale Variablen int32_t iAktuellerCountCH1 = 0; //wie viele Counts wurden auf CH1 bereits detektiert? int32_t iTeilerverhaeltnis = 2; //enthält das übertragene Teilerverhältnis ISR(PORTD_INT0_vect){ //PD0 = CH1_IN iAktuellerCountCH1 = iAktuellerCountCH1 + 1; if (iAktuellerCountCH1 >= iTeilerverhaeltnis){ //Ist Teilerverhältnis erreicht PORTB.OUTTGL = PORTB.OUT | 0b00000001; //Pin wird getoggelt iAktuellerCountCH1 = 0; } } void interrupt_init(void){ PMIC.CTRL = PMIC.CTRL | 0b00000111; //High, Medium und Low-Level Interrupts werden aktiviert PORTD.INT0MASK = 0b00000001; //PD0 wird Interrupt0 zugeordnet PORTD.INTCTRL = 0b00000010; //Interrupt0 ist Medium-Level PORTD.PIN1CTRL = (PORTD.PIN1CTRL & 0b11111000); //triggere auf steigende und fallende Flanke } void port_init(){ PORTD.DIRTGL = 0b00001000; //PD0 = CH1_IN, PD2 = RXD0, PD3 = TXD0 PORTB.DIRTGL = 0b00000001; //PB0 = CH1_OUT } int main(void){ cli(); //Interrupts global deaktivieren port_init(); clock_init(); pll_init(); sei(); //Interrupts global aktivieren interrupt_init(); while(1){ } }
>PORTB.OUTTGL = PORTB.OUT | 0b00000001; //Pin wird getoggelt
Das muss anders gehen, so liest du ja erst den Port wieder ein.
Gruß J
Vielen Dank für die rasche Antwort. Da hast du recht. Trotzdem erklährt das nicht wieso die ISR so viel Zeit verpulvert. Ist mir nach wie vor ein Rätsel.
Welcher Compiler, welche Optimierung? Kann es sein, dass der UART-Interrupt Deinen INT0-Interrupt kurzfristig sperrt? Dein INT0-Interrupt wird bei beiden Flanken aufgerufen, bei fin = 178khz also mit 356khz. Das macht bei 32Mhz 89 Takte. Bei fehlender Optimierung könnte das schon knapp werden. Gruß, Stefan
Fritz M. schrieb: > bis fin ca. 178KHz, danach kommt der Controller > nicht mehr nach Eine ISR hat immer eine Menge zusätzlichen Code zum Sichern und Rücksetzen der verwendeten Register. Siehe Disassembly. Schnelles Pintoggeln erfordert z.B. einen Timer im PWM Mode. fpga schrieb: > PORTB.OUTTGL |= PIN0_bm; Wieso nicht
1 | PORTB.OUTTGL = PIN0_bm; |
Mit dem |= ist da ein unnötiger zusätzlicher Lesezugriff drin. Fritz M. schrieb: > void port_init(){ > PORTD.DIRTGL = 0b00001000; //PD0 = CH1_IN, PD2 = RXD0, PD3 = TXD0 > PORTB.DIRTGL = 0b00000001; //PB0 = CH1_OUT > } Die Verwendung von DIRTGL ist hier IMHO komplett falsch. Du wolltest vermutlich DIRSET haben. Ich hätte zusätlich einen DIRCLR für die Input Pins gemacht - nur zur Sicherheit.
Dreh den Takt auf 60MHz (laut Atmel erlaubt) oder 64MHz (mach ich immer) hoch ;-)
Vielen Dank für die Antworten. Ist "PORTB.OUTTGL |= PIN0_bm;" nicht dasselbe wie "PORTB.OUTTGL = PORTB.OUTTGL | PIN0_bm;"? Dann würde genau gleich zuerst gelesen werden, da maskiert wird. Ich glaube nicht das dies einen Unterscheid macht. > Welcher Compiler, welche Optimierung? Öhm.. Wo kann ich das anschauen? Ich programmiere in Atmelstudio 7 (Version 7.0.1188), standartmässige Projekteinstellungen (AVR/GNU C Compiler). > Kann es sein, dass der UART-Interrupt Deinen INT0-Interrupt kurzfristig > sperrt? Der UART-Interrupt wird zurzeit nicht ausgeführt und kann daher nichts blockieren, da bin ich mir sicher. > Eine ISR hat immer eine Menge zusätzlichen Code zum Sichern und > Rücksetzen der verwendeten Register. Siehe Disassembly. Gemäss Datenblatt genau 6 Taktzyklen wenn ich das richtig verstehe: The interrupt response time for all the enabled interrupts is three CPU clock cycles, minimum; one cycle to finish the ongoing instruction and two cycles to store the program counter to the stack. After the program counter is pushed on the stack, the program vector for the interrupt is executed. The jump to the interrupt handler takes three clock cycles. > Die Verwendung von DIRTGL ist hier IMHO komplett falsch. Du wolltest > vermutlich DIRSET haben. Ich hätte zusätlich einen DIRCLR für die Input > Pins gemacht - nur zur Sicherheit. Da hast du recht, vielen dank. > Dreh den Takt auf 60MHz (laut Atmel erlaubt) oder 64MHz (mach ich immer) > hoch ;-) Also gemäss meinem Datenblatt kann der ATxmega32A4U @3.3V maximal 32MHz. Falls du ein anderes Datenblatt hast, kannst du mir den Link senden?
Fritz M. schrieb: > Gemäss Datenblatt genau 6 Taktzyklen wenn ich das richtig verstehe: Da ist aber noch kein einziges Arbeitsregister auf den Stack gesichert. Und der Rücksprung inkl. dazugehörigem Register zurückholen darf in der Rechnung auch nicht vergessen werden. Ist der eigendliche Sinn wirklich nur ein Teilen einer Eingangsfrequenz? Das lässt sich wahrscheinlich durch die interne Timer-Logik bewerkstelligen lassen, ohne einen einzigen Interrupt. Viele Grüße, Stefan
> Ist der eigendliche Sinn wirklich nur ein Teilen einer Eingangsfrequenz? Ja, allerdings mit zwei Kanälen gleichzeitig, welche eine unbekannte Phasenverschiebung (CH1->CH2: 30-70°) aufweisen. Da ich aber schon mit einem Kanal weit vom Ziel entfernt bin, habe ich den zweiten Kanal mal auskommentiert. > Das lässt sich wahrscheinlich durch die interne Timer-Logik > bewerkstelligen lassen, ohne einen einzigen Interrupt. Hmm... Hast du mir da einen Tipp? Code-technisch meine ich. Examples oder so..
Fritz M. schrieb: > int32_t iAktuellerCountCH1 = 0; > int32_t iTeilerverhaeltnis = 2; Die Variablen-Breite ist womöglich etwas zu gross, je nachdem wie dein zu erwartender Teilerbereich ist. Das kostet ein paar wertvolle Zyklen bei Vergleichen und Inkrementierungen. Vielleicht reicht sogar uint8_t ?
Fritz M. schrieb im Beitrag #5025 >> Das lässt sich wahrscheinlich durch die interne Timer-Logik >> bewerkstelligen lassen, ohne einen einzigen Interrupt. > > Hmm... Hast du mir da einen Tipp? Code-technisch meine ich. Examples > oder so.. Die oder einige Timer kannst du extern takten. Dann stellst du im Timer-OCR deinen Teilerfaktor ein und lässt den zugehörigen Ausgangspin bei erreichen des OCR-Wertes toggeln. Geht völlig ohne Interrupts. Die Timer haben aber nur 16Bit. Sascha
Fritz M. schrieb: > Ist "PORTB.OUTTGL |= PIN0_bm;" nicht dasselbe wie "PORTB.OUTTGL = > PORTB.OUTTGL | PIN0_bm;"? Dann würde genau gleich zuerst gelesen werden, > da maskiert wird. Ich glaube nicht das dies einen Unterscheid macht. Bei Portx.OUTTGL werden die Bits mit 1 gewechselt. Beim lesen hat das Register 0 da das nicht den Inhalt von Portx.OUT hat. Es reicht also
1 | PORTB.OUTTGL = Pin1_bm; |
Was für eine Frequenz hast du am Eingang. Ggf kannst du das Eventsystem nutzen. Um den Takt zuerfassen. Gruß JackFrost
:
Bearbeitet durch User
Fritz M. schrieb: > Dreh den Takt auf 60MHz (laut Atmel erlaubt) oder 64MHz (mach ich immer) > hoch ;-) > > Also gemäss meinem Datenblatt kann der ATxmega32A4U @3.3V maximal 32MHz. > Falls du ein anderes Datenblatt hast, kannst du mir den Link senden? Es gibt eine Appnote von Atmel (die ich natürlich grad nicht finde) in der beschrieben wird, wie und daß man auf 60MHz gehen kann. Ich mach dann immer gleich 64MHz (XMega256A3U).
Fritz M. schrieb: > //triggere auf > steigende und fallende Flanke Warum? Damit hast Du bei jeder Flanke einen Interrupt mit Verarbeitung, Prolog und Epilog und Deinen 32-Bit-Operationen. Wenn Du Dein .lss-File zeigst kann man schauen, wieviele Zyklen da jeweils verbraten werden. Ungefähr 80 Zyklen scheinen mir da nicht ganz so ungewöhnlich zu sein.
Fritz M. schrieb: > //Globale Variablen > int32_t iAktuellerCountCH1 = 0; //wie viele Counts wurden auf CH1 > bereits detektiert? > int32_t iTeilerverhaeltnis = 2; //enthält das übertragene > Teilerverhältnis Praktisch wäre auch, wenn Du diese Variablen als "volatile" deklarieren und ein compilierbares Beispiel Deines Problems anhängen würdest (mit genauer Angabe des MC - XMega32 gibt es in mehreren Versionen ...)
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.