Byte seriell senden ohne Serial.lib/Serial.print

Hallo Forengemeinde,

ich möchte einen Attiny2313/4313 als einfachen Tastatur-Encoder / Serial-Umsetzer einsetzen.
Die tastatur hat ne 10x5 Matrix, dies jedoch nur zur info. Der Attiny sendet die Columm-Signale und scannt die Row-Leitungen ein. eine gedrückte taste wird dann ,(bis auf die Umlaute) , in ASCII Code Umgesetzt (nur 1 Byte) und dan an einen Atmega1284 seriell gesendet, der die tastatureingaben dann weiter verarbeitet. Die Shift und CAPS-Lock Tasten werden direkt im Attiny ausgewertet und bearbeitet.
Und genau da liegt das Problem: für die CAPS-Lock LED fehlt mir ein Pin am Attiny.

Ich habe nun das Datenblatt des Attiny2313 konsultiert, eigentlich, um mich schlau darüber zu machen, wie ich den Resetpin als normalen digital I/O nutzen kann.

Dabei bin ich auf Seite 116 - 120 des Datenblattes auf was interessantes gestossen:

Wenn ich das richtig verstanden habe, ist es möglich, die serielle Schnittstelle durch schreiben in bestimmte Register für das Senden ODER Empfangen also auch nur für das eine initialisieren kann.
Danach scheint es also möglich zu sein, die Hardware-Serial auch nur zum Senden zu initialissieren, wodurch der RX-Pin weiterhin als digital I/O nutzbar ist.
Dasselbe ist auch mit dem TX pin Möglich, indem man nur den Empfangsmodus initialisiert.

Folgendes Beispiel zur initialisierung der seriellen Schnittstelle und zum Senden eines Bytes hab ich aus dem Datenblatt:

void setup() {
  // put your setup code here, to run once:
  USART_Init(9600);
}

void USART_Init( unsigned int baud )
{
                                                             
  UBRRH = (unsigned char)(baud >> 8);      /* Set baud rate */
  UBRRL = (unsigned char)baud;                                                          
  UCSRB = (1 << RXEN) | (1 << TXEN);      /* Enable receiver and transmitter */                                                             
  UCSRC = (1 << USBS) | (3 << UCSZ0);    /* Set frame format: 8data, 2stop bit */
}



void USART_Transmit( unsigned char data )
{                                                            
                                                         /* Wait for empty transmit buffer */
while ( !( UCSRA & (1 << UDRE)) )    
    ;
  UDR = data;                                    /* Put data into buffer, sends the data */
}



void loop() {
  // put your main code here, to run repeatedly:

}

Nun habe ich 2 Fragen:

  1. habe ich das Datenblatt dahingehend richtig verstanden, dass ich also den RX-Mudus sowie den TX-Modus seperat initialisieren kann, ohne dass der Nich gebrauchte Pin von der Schnitstelle gesperrt wird?

  2. Wenn ich das zu 1 richtig verstanden habe, dann kann ich im void USART_Init in der Zeile
    UCSRB = (1 << RXEN) | (1 << TXEN);
    einfach das
    (1<<RXEN) | (1<<TXEN); durch (0<<RXEN) | (1<<TXEN);
    ersetzen, damit NUR der Sende-Modus aktiv ist?

ich bin leider bei direkten Registerzuweisungen noch immer etwas sehr unsicher. Zudem sind grad meine Attinys noch unterwegs und kommen frühestens in ner Woche bei mir an. wenn ich aber jetzt schon sicher sein kann, dass das so in der Software klappt, und somit mei Pin-Problem gelöst ist, dann kann ich zumindest weiter an den Schaltplänen arbeiten.

Für euren Input bin ich sehr Dankbar.
Liebe Grüsse
Stefan

(deleted)

Moin Stefan,
wenn weiter keine anderen Bits in UCSRB gesetzt sind oder (unabsichtlich) verändert werden sollen geht das so.
Das 0 << RXEN kannst Du aber wegsparen.

Wenn Du sicher sein willst, dass nur jeweils einzelne Bits in einem Byte manipuliert werden, solltest Du mit dem Komplement verUNDen (zum Löschen).

uint8_t wert;
wert &= ~(1 << RXEN);  // lösche Bit RXEN
wert |=  (1 << TXEN);  // setze Bit TXEN

Gruß Walter

Hallo Walter,
Danke für deine Hilfe.
Wenn ich Dich richtig verstehe, würde es also genügen, wenn ich einfach schreibe:

UCSRB |= (1 << TXEN);

und wenn ich ganz sicher gehen will, dass RXEN wirklich auf 0 ist, dann einfach noch

UCSRB &= ~(1<<RXEN);

dazu?

Und noch kurz ne frage: Geht dies auch in einer einzigen zuweisung? Also, dass ich UCSRB RXEN auf 0 und auch gleich TXEN au 1 setzen kann, so wi ich auch beide in einer einzigen Zuweisung auf 1 setzen konnte? (frage das jetzt nur interessehalber, wie dann die Zuweisung aussehen müsste)

LG Stefan

Geht dies auch in einer einzigen zuweisung?
wie dann die Zuweisung aussehen müsste

UCSRB = (hier Wert zusammendengeln);

:o :o :o :o :o

combie: UCSRB = (hier Wert zusammendengeln);

:o :o :o :o :o

Halllo combie, ja genau bei dem 'hier wert zusammendengeln' fängt leider mein Problem erst richtig an! denn genau da steckt die Frage: und wie mach ich das?

LG Stefan

Und woher soll ich wissen, was du in das Register schreiben willst?

Du selbst hast folgendes ja schon gepostet!

UCSRB = (1 << RXEN) | (1 << TXEN);

Was gefällt dir daran nicht?

 UCSRB = (0 << RXEN) | (1 << TXEN);
 UCSRB =  (1 << TXEN);
 UCSRB =_BV(TXEN);

Fehlt dir ein C++ Buch?
Das Datenblatt?
Eigentlich nicht, oder…

Deltaflyer: Und noch kurz ne frage: Geht dies auch in einer einzigen zuweisung? Also, dass ich UCSRB RXEN auf 0 und auch gleich TXEN auf 1 setzen kann, so wie ich auch beide in einer einzigen Zuweisung auf 1 setzen konnte?

Das geht nur dann, wenn Du auf der rechten Seiten den (alten) Wert des Registers verwendest um - wie combie so schön formulierte - "den Wert zusammen[zu]dengeln".

Im kompilierten Code ergibt das am Ende keinen Unterschied; diese eine Zeile wäre dann m.E. einfach nur schwieriger zu lesen als der Zweizeiler mit getrenntem Setzen und Löschen.

OT: Mit der Auffassung bin ich aber vielleicht nur recht einfach gestrickt und altmodisch; ich habe in der Fa. Kollegen, die mit möglichst wenig Codezeilen am besten schon in C++23 möglichst große Meisterschaft demonstrieren wollen.

Hallo combie,

Ja , das mit dem

 UCSRB = (0 << RXEN) | (1 << TXEN);

War ja auch meine erste Idee. Und gefallen tut mir das auch.

 UCSRB =  (1 << TXEN);

Gefällt mir auch, wenns damit wirklich getan ist, gerne.

UCSRB =_BV(TXEN);

Muss ich mich erst noch mit dem ‘_BV’ schlau machen , das kenne ich noch nicht.

Ich habe aber dann beim Lesen der Antwort von wno158 wohl kurz nen Hänger in der CPU zwischen meinen
Ohren gehabt, und ihn da etwas missverstanden, oder falsch interpretiert.

Ja, ein C++ Buch fehlt mir.
Das datenblatt hab ich mir angeschaut, aber war unsicher, ob ich’s richtig verstanden hab, darum hab ich ja überhaupt erst hier nachgefragt.

Aus gesundheitlichen Gründen habe ich manchmal sehr sehr grosse Lernschwiergkeiten und Verständnisprobleme. besonder stark wirkt sich das aus, wenn ich mal wieder, wie letztes Wochenende, im Schlaf in eine tiefe, mehrstündige Bewusstlosigkeit gefallen bin. Danach brauche ich oft sehr sehr lange, bis ich wieder alles einigermassen zusammen bekomme.
Darum sorry, dass ich so blöd gefragt habe, und danke für Deine ausführliche Hilfe.

LG Stefan

Bei Bitfummeleien mit Registern muß man ggf. berücksichtigen, daß sich manche Bits gegen Änderungen wehren, oder gleich wieder umgeschrieben werden, oder das Lesen oder erneutes Schreiben (ohne Änderung) schon irgendwas auslöst. Deshalb würde ich auch getrennt angeben was gesetzt und was gelöscht werden soll.

Nur die aktuell anstehenden Bits zu ändern ist Harakiri, weil dabei alles andere auf 0 gestzt wird, was ziemlich sicher nicht richtig ist.

Frage an die Experten: gibt es nicht Befehle, die direkt ein Bit in einem Register setzen oder löschen?

Hallo Walter,

Ja, an und für sich spielt es für mich auch keine Rolle, ob ich es in einer Zeile, oder in 2 Zeilen löse. besonders, wenn der daraus resultierende compilierte Code gleich gross ist.

Jetzt in diesem Anwendungsfall sollte es eigentlich ausreichen, NUR TXEN zu aktivieren, da die Serielle Schnitstelle nach dem aufstarten des Attinys ja sowieso deaktiviert ist und die RX/TX Pins als normale digitale I/O initialisiert sind, respektive im hochohmigen Zustand, bis sie zugewiesen werden. Und hier macht der Attiny ja auch gar nix anderes, als als Tastatur-Encoder zu fungieren. Ich muss also nicht befürchten, dass ich da die Serielle Schnittstelle doch irgendwan mal für den Empfang einrichten müsste.

Auch Dir nochmals ein herzliches Dankeschön.

Jetzt kann ich weiter am Schaltungsentwurf für das Gerät arbeiten.

LG Stefan

Frage an die Experten: gibt es nicht Befehle, die direkt ein Bit in einem Register setzen oder löschen?

Ja.
Assembler Statements

Wenn es dem Kompiler möglich ist, nutzt er diese, egal was du da schreibst.

 UCSRB |= (1 << TXEN);

Wird eingedampft zu:

sbi 0x0a, 3

Ganz automatisch.
Ohne alle Klimmzüge.


  UCSRB = (0 << RXEN) | (1 << TXEN);

Wird übrigens zu:

ldi	r24, 0x08	
out	0x0a, r24

Danke, so etwa hatte ich mir das vorgestellt.