Hi, ich möchte mit meinem MSP430F1612 alle 2us den Port 6.1-6.7 einlesen und in einen Buffer schreiben (Triggerung mit Interrupt an Port 2.1). Der MSP430 läuft mit einer Frequenz von ca. 12MHz. Clock-Initialisierung hier void SpeedUpClock(void) { BCSCTL2 |= DCOR; // Rosc BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // XT2on, max RSEL DCOCTL = 0xff; _BIS_SR(OSCOFF); // XTAL not used } Mit Hilfe folgender Interrupt-Routine wird der Port 6 eingelesen und der Wert in einen Buffer geschrieben. // Port 2 interrupt service routine #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { // P4OUT ^= 0x08; // Toggle P4.3 *(PORTCODES + a) = P6IN; // Port 6 einlesen P2IFG &= ~0x02; // P2.1 IFG cleared a++; } Mit dieser Methode kann ich die Interrupt-Routine sicher mit einem Triggersignal von 4us an Port 2.1 "anspringen"! Fragen: 1. Hat jemand eine Idee wie ich das noch optimieren kann, um auf ca 2us zu kommen? Mfg MSP430-FAN
Ich kenne die Architektur nicht, aber u.U. geht Polling noch was schneller... der Controller kann bei deiner Taktung wahrscheinlich eh nichts wirklich Sinnvolles mehr nebenher machen. Ansonsten auf Assembler umsteigen, da lässt sich mit Sicherheit noch was rausholen. Ansonsten das 'a++' in '*(PORTCODES + a) = P6IN;' reinziehen. Vielleicht spart das nochmal einen Cycle beim Optimieren. Oder 'a' mit 'PORTCODES' statt mit 0 initialisieren, spart u.U. eine weitere Addition. Nur ein par Ideen - ob das was du da tust sinnvoll ist sei mal dahingestellt...
Ach ja, was macht P2IFG &= ~0x02; // P2.1 IFG cleared aktiviert das die Interrupts wieder? Macht der Controller das nicht automatisch beim Verlassen des Handlers? Lass in diesem Falle die Zeile besser weg. Die paar Zyklen, die der Interrupt-Handler früher wieder verfügbar ist, rächen sich mit einem Stack-Overflow, wenn der Controller wirklich davon Gebrauch macht!
12MHz bei einer CPU die für max 8MHz spezifiziert ist? Mutig. Naja, in den Interrupt springen benötigt 6 Zyklen (bei 12MHz 500ns), Interrupt verlassen benötigt 5 Zyklen (416ns). Sind zusammen 11 Zyklen bzw. knapp 1µs. Sooo...dann hast du für die Abarbeitung deiner Befhle da nur noch 13 Zyklen Zeit. Allerhöchstens, denn ich weiß nicht, ob der ohne Verzögerung nach dem RETI wieder den neuen Interrupt anspringt. Das wird haarig. Und ja, das Pin-Interrupt Flag muss man selbst löschen, das ist nur die Anzeige, welcher Pin ausgelöst hat, nicht das Interrupt Enable.
Danke für die Infos! Hat jemand eine Assembler Referenz mit der Angabe von Clock-Zyklen pro Befehl?
Ich würd mal sagen: Vergiss es per IRQ! Wie schon gesagt wurde, hast Du 24 Taktzyklen (TZ) zur Verfügung (2µs bei 12MHz -> 24TZ) 11 TZ verbrät allein die Interruptanforderung + RETI, bleiben Dir also effektiv noch 13TZ übrig. Meine schnellste Assemblerlösung lautet:
1 | ; Rx: beliebiges Register, muss einmal mit 0 initialisiert werden |
2 | ; Rx darf sonst nirgends verändert werden, nur in ISR! |
3 | ; BUF: Anfangsadresse des Buffers |
4 | MOV &P6IN, BUF(Rx) ; P6IN in Buffer kopieren (6TZ) |
5 | ADD #0x01, Rx ; Index erhöhen (1TZ) |
6 | BIC #0x02, &P2IFG ; IRQ-Flag löschen (4TZ) |
Macht zusammen 11TZ Bleiben Dir also noch ganze 2TZ übrig! Da aber nach Rücksprung aus der ISR mindestens ein Befehl im "normalen" Programm ausgeführt wird, bevor wieder in die ISR gesprungen wird, und dieser Befehl durchaus länger als 2TZ dauern kann, ist Dein gefordertes Timing meines Erachtens so nicht zu erreichen! Selbst MSP430F2xxx @ 16MHz scheint mir recht knapp bemessen!
Mögliche Ansatz mit Polling (ohne Gewähr)
1 | LOOP: BIT #0x02, &P2IFG ; IRQ-Flag pollen (4TZ) |
2 | JEQ LOOP ; Loop, solagen kein IRQ (2TZ) |
3 | MOV &P6IN, BUF(Rx) ; P6IN in Buffer kopieren (6TZ) |
4 | ADD #0x01, Rx ; Index erhöhen (1TZ) |
5 | BIC #0x02, &P2IFG ; IRQ-Flag löschen (4TZ) |
6 | CMP #N, Rx ; Abbruch nach N durchläufen (1 oder 2TZ) |
7 | JNE LOOP ; Loop (2TZ) |
8 | ... ; weiter nach Abbruch... |
wären dann 20 oder 21TZ (je nach N). Könnte vielleicht klappen :-)
Hi, ich kam auch schon auf die Idee die ISR in assembler zu implementieren. Mit dem IAR kann ich CPU Register freihalten (z.B R4 und R5). Dieses Prozessorregister kann ich dann für den INC im Buffer verwenden. Wie sollte die ISR in Assembler aussehen? // Port 2 interrupt service routine #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { *( PORTCODES + a ) = P6IN; // Port 6 einlesen P2IFG &= ~0x02; // P2.1 IFG cleared a++; } Vorallem wie bringe ich es dem IAR als inline assembler bei? Ciao und Danke
Was ist überhaupt mit einer Abbruch-Bedingung? Irgendwo muss der Interrupt ja mal freigegeben und gesperrt werden? Der Prozessor soll ja sicher noch andere Sachen machen und nicht unendlich lange den Port-Status in ein Register sortieren. Oder wie dachtest du das? Das sind ja auch nochmal Befehle, die abgearbeitet werden müssen. Und wieso gerade einen MSP430 für sowas schnelles? Den 50% über seiner max. Taktfrequenz zu betreiben wird eh nicht sehr zuverlässig funktionieren, der Prozessor macht dann manchmal komische Sachen.
Zur Info was ich gerade probieren möchte: 1. Ich habe einen PC der mir auf dem LPC-Bus Port 80h ausgaben macht. 2. Am LPC-Bus hängt ein PLD der die Ausgaben decodiert und an einer 7-Segment-Anzeige die Port 80h codes anzeigt. 3. Mit dem MSP möchte ich die Codes an Port 6 einlesen 4. Über die serielle Schnittstelle des MSPs möchte ich dann die Port 80h Codes an einen Host-Rechner senden Ciao
>Wie sollte die ISR in Assembler aussehen?
Beratungsresistent ? ;-)
Ich habe doch schon dargelegt, warum eine ISR-Lösung nicht funktionieren
kann!
Und 'ne Alternative hab ich Dir auch schon vorgeschlagen...
Hi Stefan, ich habe deine Assembler Beispiele gesehen. Ich möchte es auch so probieren. Aber wie bringe ich es dem IAR compiler bei? Ich wollte "inline assembler" Befehle verwenden. Mein Hauptprogramm sollte schon in C codiert sein. Ich will nur die ISR in assembler. Ciao
>Aber wie bringe ich es dem IAR compiler bei?
Schon mal den "C/C++ Compiler Reference Guide" konsultiert?
Stichwort "Inline Assembler"
Wie wäre es denn mit der DMA-Engine des MSP430. Einmal einstellen, und dann schießt der DMA die Daten vom Port in den Puffer, kümmert sich ums inkrementieren und bei Bedarf kannst du die mit einem weiteren DMA Kanal gleich auf dem UART schicken. Wenn du vorher weißt, wieviele Bytes diese Karte an den MSP sendet, kannst du das als transfer size dem DMA Controller gleich geben, der Rest läuft in Hardware dann ab.
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.