Forum: Mikrocontroller und Digitale Elektronik 8Bit PORT splitten


von Karsten K. (karsten42)


Lesenswert?

Moin Moin Wissende,

ATMEGA88 als DDS LFO. PORTD nutze ich als 8Bit Ausgabe über ein R2R 
Netzwerk.

Jetzt benötige ich jedoch den UART an PD0 und PD1 und muss deshalb den 
PORTD splitten. Über eine ISR zähle ich einfach rauf/runter und setze 
dann den entspr. Wert:
1
volatile uint8_t cnt = 0;
2
ISR(TIMER1_COMPA_vect)  {
3
4
  if(cnt >= 255)
5
    direction = DOWN;
6
7
  if(cnt <= 0)
8
    direction = UP;
9
10
  if(direction)  {
11
    PORTD = cnt--;
12
  } else {
13
    PORTD = cnt++;
14
  }
15
16
}

Um nun PORTD 7-2 und PORTB 1-0 als 8Bit Ausgang mit dem jeweiligen Wert 
von cnt zu nutzen benötige ich immer zwei Operationen pro PORT:
1) Löschen der vorherigen Bits
2) Setzen der neuen Bits
1
   // 6Bit on PORTD
2
   PORTD &= ~(0xFC);
3
   PORTD |= (cnt << 2);
4
5
   // 2 Bit on PORTB
6
   PORTB &= ~(0x3);
7
   PORTB |= (cnt >> 6);

Das ist unschön, da ja jedes mal das Ausgangssignal zunächst auf 0V geht 
( alle Bits aus ) und dann den neuen Wert erhält.

Habt Ihr vielleicht eine schlauere Lösung? Also am besten nur das delta 
setzen. AGenial wäre es natürlich auch die beiden Ports in nur einer 
Operation verändern zu können...

Daaaanke für jeden Tip!

Gruß
Karsten

von Falk B. (falk)


Lesenswert?

@Karsten K. (karsten42)

>Um nun PORTD 7-2 und PORTB 1-0 als 8Bit Ausgang mit dem jeweiligen Wert
>von cnt zu nutzen benötige ich immer zwei Operationen pro PORT:
>1) Löschen der vorherigen Bits
>2) Setzen der neuen Bits

Niemand hindert dich, die Berechnung des neuen PORT-Wertes zuerst zu 
machen und dann erst die Zuweisung an den echten Port. Damit hast du bei 
2 Ports minimal 1 Takt Versatz bei den Portzuweisungen.

>setzen. AGenial wäre es natürlich auch die beiden Ports in nur einer
>Operation verändern zu können...

Das geht nicht.

von Peter D. (peda)


Lesenswert?

Warum nimmst Du nicht den ATtiny2313/4313?
Da ist PORTB komplett verfügbar, da die UART auf PORTD liegt.

von Michael A. (micha54)


Lesenswert?

Hallo,

was hindert Dich eigentlich daran, jeden Port in nur einer Zuweisung zu 
bearbeiten ?
1
   // 6Bit on PORTD
2
   PORTD = (PORTD & (~(0xFC))) | (cnt << 2);
3
4
   // 2 Bit on PORTB
5
   PORTB = (PORTB & (~(0x3))) | (cnt >> 6);

Gruß,
Michael

von Axel S. (a-za-z0-9)


Lesenswert?

Karsten K. schrieb:
> Genial wäre es natürlich auch die beiden Ports in nur einer
> Operation verändern zu können...

Das geht mit keiner Hardware. Weswegen man ein solches Splitting wie du 
machst, ja auch möglichst vermeidet. Insbesondere wenn an den Pins eine 
externe Hardware hängt, die timing-sensitiv ist.

Die einzige Möglichkeit, das wieder sauber zu bekommen ist, ein Latch 
zwischen deinen 8-Bit "Port" und das R2R-Netzwerk zu schalten. Dann 
brauchst du natürlich noch einen 9. Pin, der dem Latch das "Daten jetzt 
übernehmen" Signal gibt. Da der Krempel ja wohl recht langsam ist, 
könntest du statt des parallelen Latches auch ein Schieberegister mit 
Latch nehmen. Siehe AVR-Tutorial: Schieberegister

von H.Joachim S. (crazyhorse)


Lesenswert?

Bliebe noch die Möglichkeit, eine Software-UART zu benutzen, dann bist 
du mit den dafür benutzen Pins frei.
Oder eine externe UART (I2C oder SPI), geht auch. Eigentlich aber nicht 
zu empfehlen, teurer als der komplette Prozessor.

Ich würde auch sagen: Prozessorwechsel.

von P. M. (o-o)


Lesenswert?

Axel Schwenke schrieb:
> Das geht mit keiner Hardware. Weswegen man ein solches Splitting wie du
> machst, ja auch möglichst vermeidet. Insbesondere wenn an den Pins eine
> externe Hardware hängt, die timing-sensitiv ist.

Es geht aber in zwei aufeinanderfolgenden Takten. Dazwischen liegen dann 
wohl wieder einige hundert Takte, bis der nächste Wert kommt. Somit ist 
diese winzige Verzögerung vernachlässigbar bzw. herausfilterbar, da die 
Frequenz des generierten Signals sicher viel niedriger ist.

von Karsten K. (karsten42)


Lesenswert?

Hey Jungs,

Ohh Daaanke für die vielen guten Tips!

Processorwechsel:
Ja, eine Option die sicher vieles einfacher macht. Ich werde darüber 
ernsthaft nachdenken!

Alles in einer Zuweisung:
Au fein! Das geht schön, DAAANKE! Bei LFO, also F <=5 Hz wird der sprung 
zum zweiten Port nicht auffallen. ich werde es austesten.

Hab ich schon gesagt, das Ihr alle waaaahnsinnig tolle Leute seid?

Daaanke für eure Mühe!

Gruß
Karsten

von Thomas E. (thomase)


Lesenswert?

Karsten K. schrieb:
> Bei LFO, also F <=5 Hz wird der sprung zum zweiten Port nicht auffallen.

Da die beiden höchsten Bits nachgereicht werden, macht sich der Versatz 
ohnehin nur 4x pro Umlauf (0, 64, 128, 192) bemerkbar. Man kann sich die 
Ausgabe auf PORTB durch Abfrage von cnt auch grundsätzlich 252x pro 
Umlauf sparen.

mfg.

von P. M. (o-o)


Lesenswert?

Karsten K. schrieb:
> Processorwechsel:
> Ja, eine Option die sicher vieles einfacher macht. Ich werde darüber
> ernsthaft nachdenken!

Die AVRs sind äusserst gute Kerle. Sehr einfach zu programmieren, sehr 
robust, praktisch kein Overhead in Soft- und Hardware. Und erstaunlich 
leistungsfähig, wenn man's richtig macht. So lange ich nicht an 
schmerzhafte Leistungsgrenzen stosse, würde ich mich nicht mit einer 
komplexeren Architektur herumschlagen wollen.

von H.Joachim S. (crazyhorse)


Lesenswert?

Darum gehts doch gar nicht. Die Option war, einen AVR zu nehmen, der 
sowohl einen kompletten 8bit-Port und dessen UART auf einem anderen Port 
liegt.

von P. M. (o-o)


Lesenswert?

H.Joachim Seifert schrieb:
> Darum gehts doch gar nicht. Die Option war, einen AVR zu nehmen, der
> sowohl einen kompletten 8bit-Port und dessen UART auf einem anderen Port
> liegt.

Achso, hab ich wohl falsch verstanden.

Aber selbst dann... Der vorliegende Fall kann locker mit dem aktuellen 
Prozessor gelöst werden. Den Aufwand eines Prozessorwechsels nur um eine 
etwas "sauberere" Lösung zu haben, würde ich mir jedenfalls nicht antun.

von H.Joachim S. (crazyhorse)


Lesenswert?

Kann man pauschal nicht sagen.
Es kommt eben auf die Anforderungen an.
Der Wechsel von z.B. 0x7F auf 0x80 verursacht einen heftigen 
kurzzeitigen Fehler (0x7F -> 0x70 -> 0x80).
Ausserdem wird die max. mögliche Ausgabefrequenz durch die mehrfachen 
Portzugriffe herabgesetzt.

Wenn das alles kein Problem ist, ist es doch gut.

von Stefan F. (Gast)


Lesenswert?

Ich bin etwas irritiert. Der serielle Port überschreibt die normale I/O 
Funktion von PD0 und PD1. Also kann man die Ausgabe einfach auf PORTB = 
(cnt << 2) reduzieren - denke ich.

von Thomas E. (thomase)


Lesenswert?

Stefan us schrieb:
> Ich bin etwas irritiert. Der serielle Port überschreibt die normale I/O
> Funktion von PD0 und PD1. Also kann man die Ausgabe einfach auf PORTB =
> (cnt << 2) reduzieren - denke ich.

Für Tx trifft das zu. Am Rx würde in diesem Fall der Pullup toggeln. Mag 
sein, dass das keinen grossen Geist stört. Schön ist es trotzdem nicht. 
Schon gar nicht, wenn die Eingangsschaltung so ausgelegt ist, dass sie 
den Pullup braucht.

mfg.

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.