Hallo Gemeinde, ich arbeite gerade an meinem ersten XMega Projekt und stehe nun vor folgender Aufgabe. Ein UART ähnliches Signal das an Pin C2 anliegt soll in 99% der Zeit unverändert auf Pin C3 und C5 wieder ausgegeben werden. Nur in machen fällen will ich - anstelle der direkten Ausgabe - selber ein Signal aus dem µC ausgeben. Nun könnte man der Einfachheit halber einen Ansatz wählen wie: while(spiegeln) {PinC2=PINC3} Allerdings ist das natürlich von der Systemlast her unsinn. Daher frage ich mich, ob man diese Aufgabe nicht vom DMA Controller erledigen lassen kann? Da ich selber noch nicht mit dem DMA gearbeitet habe weiß ich nicht, wie ich diesen Einstellen und maskieren muss, damit nur die betroffenen Pins permanent verarbeitet werden. Wobei permanent ja eigentlich auch nicht richtig ist. Im Grunde würde es reichen bei jedem Flankenwechsel den aktuellen Zustand zu spiegeln. So müsste die Systemlast auf das notwendige Minium sinken. Gruß Chris
Hi, DMA uh oh, aber ich glaube der kann nur Byte weiße Daten schieben. ABER: es müsste einen PIN Change interrupt geben bei den XMEGAS für alle Pins. MfG ich
Dann schalte C3 und C5 auf input und mit einem Analogschalter/Reed Relais/Mosfet mit C2 kurz. Nur für die 1% der Zeit unterbrichst du den Schalter, schaltest C3 und C5 aus Ausgang und gibst deine Signale aus. Nicht vergessen die Pins wieder auf Eingang/hochohmig zu Schalten bevor du den Schalter wieder einschaltest. Alternativ wie oben beschrieben ein Pin Change Interrupt, aber dann hast du die Latenzzeit bis dein Interruptcode ausgeführt wird. Wie groß die ist hängt davon ab on andere Interrupts ggf. den Pin Change sperren können.
:
Bearbeitet durch User
Schau Dir mal das Event-System an, damit könnte das elegant gehen.
Chris N. schrieb: > Ein UART ähnliches Signal Wie schnell denn (Baudrate), d.h. welcher Jitter ist erlaubt?
Die Idee mit dem externen Schalter hatte ich auch schon, ist aber irgendwie die plain and boring Lösung und ist natürlich auch wieder ein zusätzliches Bauteil. Signal ist 500 kBit/s, also recht langsam und gemütlich. Jedoch ist die Systemlast natürlich ein wichtiger Punkt, wenn man nur jedes 100000 Paket verlieren darf.
>Signal ist 500 kBit/s, also recht langsam und gemütlich.
2us pro Bit würde ich nicht als gemütlich bezeichnen.
Man muss nicht jeden Scheiß in Software lösen. Manchmal darf es auch ein bisschen Hardware sein.
Hi, Hat der XMEGA einen internen Comparator mit Ausgabe an Pin? Vllt. lässt sich darüber was basteln...
Chris N. schrieb: > Signal ist 500 kBit/s, also recht langsam und gemütlich. Für eine Hardwarelösung ja. In SW vergiß es.
Peter Dannegger schrieb: > Chris N. schrieb: >> Signal ist 500 kBit/s, also recht langsam und gemütlich. > > Für eine Hardwarelösung ja. > In SW vergiß es. Ein Xmega läuft mit maximal 32 MIPS. D.h. er führt zwischen den Flanken 64 Befehle aus. Das ist schon im Bereich des machbaren. Man muss halt etwas kreativ mit der Software spielen....
@ Max D. (max_d) >Ein Xmega läuft mit maximal 32 MIPS. D.h. er führt zwischen den Flanken >64 Befehle aus. Das ist schon im Bereich des machbaren. Man muss halt >etwas kreativ mit der Software spielen.... Stimmt schon. OK, sind wir mal kreativ. Nehmen wir SPI und lesen einfach das Datenpin ein und schieben es gleich wieder über SPI raus. Macht bei F_CPU/2 16 Msmps, bzw. etwas mehr 0,5us Verzögerung. Mit dem DMA kann man das umkopieren wahrscheinlich machen. Wobei das nur bei einem UART im SPI-Mode geht, das SPI-Modul ist nur im Slave Mode DMA-fähig (warum auch immer 8-0) Auf einem beliebigen Pin wird das aber eher schwierig.
Max D. schrieb: > Ein Xmega läuft mit maximal 32 MIPS. D.h. er führt zwischen den Flanken > 64 Befehle aus. Das ist schon im Bereich des machbaren. Es reicht aber nicht, wenn keine Flanke verloren geht, die Pulsdauer sollte auch einigermaßen stimmen. Bei 10% Jitter ist die Erkennbarkeit der UART schon deutlich herabgesetzt. 10% wären 6 Zyklen und selbst ein nackter Interrupt dauert schon 10. Idealer Weise sollte eine Abtastung mit 16*Baudrate erfolgen, damit die Störerkennung der UART noch greift (Abtastung bei 7/16, 8/16 und 9/16 Bitzeit).
:
Bearbeitet durch User
Hallo TO, ja das geht (fast) wie gedacht. Mit Event-System und DMA.
1 | uint8_t serdata = PIN3_bm | PIN5_bm; |
2 | |
3 | void pin_pass (void) { |
4 | |
5 | // Signal PC2 an PC3 und PC5 weiterleiten. Eventgesteuert per DMA
|
6 | // Pro Flanke ein Durchlauf des DMA_CH0 Interrupts
|
7 | |
8 | EVSYS.CH0MUX = EVSYS_CHMUX_PORTC_PIN2_gc; // Pin auswählen |
9 | EVSYS.CH0CTRL = EVSYS_DIGFILT_8SAMPLES_gc; |
10 | |
11 | // Ausgangspins vorbereiten und richtigen Pegel ausgeben
|
12 | PORTC.DIRSET = PIN3_bm | PIN5_bm; |
13 | if (PORTC.IN & Pin2_bm) PORTC.OUTSET = PIN3_bm | PIN5_bm; |
14 | else PORTC.OUTCLR = PIN3_bm | PIN5_bm; |
15 | |
16 | // reset DMA controller
|
17 | DMA.CTRL = 0; |
18 | DMA.CTRL = DMA_RESET_bm; |
19 | while ((DMA.CTRL & DMA_RESET_bm) != 0); |
20 | |
21 | // configure DMA controller
|
22 | DMA.CTRL = DMA_CH_ENABLE_bm | DMA_DBUFMODE_DISABLED_gc; |
23 | |
24 | // reset DMA-Channel
|
25 | DMA.CH0.CTRLA |= (1<<6); |
26 | while( DMA.CH0.CTRLA & (1<<6)); |
27 | |
28 | DMA.CH0.REPCNT = 0; |
29 | DMA.CH0.CTRLA = DMA_CH_BURSTLEN_1BYTE_gc | DMA_CH_REPEAT_bm; // |
30 | DMA.CH0.CTRLB = DMA_CH_TRNINTLVL_OFF_gc; |
31 | DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_TRANSACTION_gc | DMA_CH_SRCDIR_FIXED_gc | // reload source after every transaction |
32 | DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_FIXED_gc; // reload dest after every transaction |
33 | DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_EVSYS_CH0_gc; |
34 | DMA.CH0.TRFCNT = 1; // always the number of bytes, even if burst length > 1 |
35 | DMA.CH0.DESTADDR0 = (( (uint16_t) &PORTC.OUTTGL) >> 0) & 0xFF; |
36 | DMA.CH0.DESTADDR1 = (( (uint16_t) &PORTC.OUTTGL) >> 8) & 0xFF; |
37 | DMA.CH0.DESTADDR2 = 0; |
38 | DMA.CH0.SRCADDR0 = (( (uint16_t) serdata) >> 0) & 0xFF; |
39 | DMA.CH0.SRCADDR1 = (( (uint16_t) serdata) >> 8) & 0xFF; |
40 | DMA.CH0.SRCADDR2 = 0; |
41 | DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; |
42 | }
|
43 | |
44 | ISR(DMA_CH0_vect) { |
45 | DMA.CH0.CTRLB |= (DMA_CH_TRNIF_bm | DMA_CH_ERRIF_bm); |
46 | DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; |
47 | }
|
Zum selbst steuern der Pins den DMA-CH abschalten. Nach dem selbst steuern, die Pegel wieder wieder wie den Quell-Pin setzten und den DMA-CH einschalten. Wenn aber beim neu initialisieren zwischen Einlesen des Pegels am Quell-Pin bis zum Einschalten des DMA eine Flanke erfolgt, wird es zu einem invertierten Ausgangssignal kommen, da der DMA ins Toggle-Register schreibt und diese Flanke verloren ging. Da muss man also schauen, wie das Timing passt. Gruss
Andre S. schrieb: > ja das geht (fast) wie gedacht. > Mit Event-System und DMA. Wäre mal interessant, ob das auch in der Praxis klappt und die 500kBit ohne größere Jitter oder Ausfälle durchkommen. Und auch, wenn noch andere (low-Priority) Interrupts behandelt werden müssen.
Asche über mein Haupt, ich hab mal wieder meinen eigenen Code nicht richtig gelesen. Hab mir das noch mal angeschaut:
1 | DMA.CH0.CTRLB = DMA_CH_TRNINTLVL_OFF_gc; |
Es wird der DMA-CH-Interrupt gar nicht benutzt. Der Channel läuft im Endlos-Mode, getriggert vom Event-Channel. Das geht, da die Src- und Dest-Adressen nicht geändert werden müssen. Jitter von Flanke zu Flanke hängt (eigentlich) nur davon ab, wie schnell der DMA-Controller nach Trigger Zugriff auf das Bussystem bekommt. Bei 125kbps funktioniert das bei mir. 500kbps ist aber schon eine Ansage. In der Tat sollte man das Timing kontrollieren. Nachtrag: Hab auf dem Steckboard mal nachgeschaut (war noch aufgebaut): Aus dem Funktionsgenerator 250kHz -> Es sind Ausreisser bis 2,6µs dabei. (Sowohl Pulse aus auch Pausen) Fazit: Geht wohl bis 125kbps, danach ist die Hardware für diese Anwendung am Ende, und die Puls/Pausenbreite weicht zu sehr ab.
Falls dir ein Widerstand nicht zu viel HW ist: Das Eingangssignal kommt an C2 und an einen Widerstands (etwa 1K). Den anderen Anschluss des Widerstands kommt an C3 (dieser muss Tristate fähig sein!). C3 bleibt in den 99% hochohmig. Falls du das Signal ändern willst, schaltest du C3 als Ausgang und schickst deine Daten raus. Anders ausgedrückt: Normalerweise wird das Signal über den Widerstand weitergeleitet (unverändert). Ansonsten überschreibst du mit C3 das Signal. (C5 hab ich mal weggelassen)
Stefan1234 schrieb: > Falls dir ein Widerstand nicht zu viel HW ist: > Das Eingangssignal kommt an C2 und an einen Widerstands (etwa 1K). Den > anderen Anschluss des Widerstands kommt an C3 (dieser muss Tristate > fähig sein!). C3 bleibt in den 99% hochohmig. Falls du das Signal ändern > willst, schaltest du C3 als Ausgang und schickst deine Daten raus. Oder die schöne Variante davon, einen Tristate-Buffer über den Enable geschaltet und den uc-Pin passend komplementär als Tristate.
Stefan1234 schrieb: > Falls dir ein Widerstand nicht zu viel HW ist: Nach drei Monaten wird er's doch hoffentlich geschafft haben, den Pinzustand zu spiegeln. Außerdem ist natürlich klar, dass man sowas mit Hardware immer erschlagen kann; die Lösung rein in Software war deshalb die Herausforderung.
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.