Xmega Pin Zustand effizient spiegeln

Gast #3901611
Lesenswert?

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
#3901639
Lesenswert?

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.
Gast #3908438
Lesenswert?

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.
#3908954
Lesenswert?

@ 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.
#3908961
Lesenswert?

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).
#3957072
Lesenswert?

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
#3962712
Lesenswert?

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.
Gast #4106567
Lesenswert?

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)
#4127468
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren