Forum: Compiler & IDEs Xmega Pin Zustand effizient spiegeln


von Chris N. (Gast)


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

von ich (Gast)


Lesenswert?

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

von Udo S. (urschmitt)


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.

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Schau Dir mal das Event-System an, damit könnte das elegant gehen.

von Peter D. (peda)


Lesenswert?

Chris N. schrieb:
> Ein UART ähnliches Signal

Wie schnell denn (Baudrate), d.h. welcher Jitter ist erlaubt?

von Chris N. (Gast)


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.

von holger (Gast)


Lesenswert?

>Signal ist 500 kBit/s, also recht langsam und gemütlich.

2us pro Bit würde ich nicht als gemütlich bezeichnen.

von Falk B. (falk)


Lesenswert?

Man muss nicht jeden Scheiß in Software lösen. Manchmal darf es auch ein 
bisschen Hardware sein.

von Stefan1234 (Gast)


Lesenswert?

Hi,

Hat der XMEGA einen internen Comparator mit Ausgabe an Pin? Vllt. lässt 
sich darüber was basteln...

von Stefan1234 (Gast)


Lesenswert?

oder irgend ein Timer Output Match Compare Gedöns

von Peter D. (peda)


Lesenswert?

Chris N. schrieb:
> Signal ist 500 kBit/s, also recht langsam und gemütlich.

Für eine Hardwarelösung ja.
In SW vergiß es.

von Max D. (max_d)


Lesenswert?

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

von Falk B. (falk)


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.

von Peter D. (peda)


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).

: Bearbeitet durch User
von Andre S. (lighthammer)


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

von Peter D. (peda)


Lesenswert?

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.

von Andre S. (lighthammer)


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.

von Stefan1234 (Gast)


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)

von Moritz A. (moritz_a)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
Noch kein Account? Hier anmelden.