Forum: Compiler & IDEs UART Problem mit Initialisierung


von David (Gast)


Lesenswert?

Hallo :)

ich bin ein richtiger newbie in sachen Mikrocontrollern. Zwar sind wir 
in der FH schon paar Wochen dabei, aber ohne Vorkenntnisse ist es eben 
schwieriger ;)

So nun zu meinem Problem. Wir müssen per UART-Schnittstelle Zeichen an 
ein Hyperterminal senden und danach ein Echoprogramm schreiben.
Das alles in C, ohne Interruptfunktion, sondern per polling. Wir 
benutzen einen Atmega32 mit einer Taktfrequenz von 16MHz.
Soweit so gut. Ich möchte ich die Betriebsart im UCSRC Register 
einstellen.
8N1 -> 8 Zeichenbits, kein Paritätsbit, 1 Stoppbit.
Wenn ich nun die Zeichenbits einstelle, dann setzt der Simulator im 
Register UBRRH die Bits 1 und 2 sofort auf 1.
Also synchron zum UCSRC register.
Woran kann das liegen?
Benötige dringen hilfe :S

Hier ein Auszug aus dem Quellcode:

void usart_init(int Systemtakt, long Baudrate, int ZBits, int SBit, int 
PBit) {

  int Teiler = ((Systemtakt*1000000) / (Baudrate*16)) - 1;

    UBRRH = (unsigned char)(Teiler>>8);
    UBRRL = (unsigned char) Teiler;

  UCSRB = (1<<TXEN);
  UCSRC = (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);

  if (ZBits == 8) {
    UCSRC |= (1<<UCSZ0);
    UCSRC |= (1<<UCSZ1);
  }

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> void usart_init(int Systemtakt, long Baudrate, int ZBits, int SBit, int
>   int Teiler = ((Systemtakt*1000000) / (Baudrate*16)) - 1;

Ergibt Integeroverflow bei der Berechnung von Systemtakt*1000000.

Ihr müsst hier den Rechenbereich beachten und bei der Berechnung auf 
ausreichend großen Datentypen durchführen.

   // Systemtakt hier in MHz (16).
   int Teiler = ((Systemtakt*1000000UL) / (Baudrate*16UL)) - 1;

Teiler sollte vom Datentyp unsigned int sein.

Es ist IMHO sinnfrei den Systemtakt als Variable zu übergeben und dann 
auch noch als Ganzzahl.

Der Systemtakt ist zur Kompilezeit i.d.R. bekannt und wird üblicherweise 
als Makro benutzt (F_CPU).

Warum stört mich die Ganzzahl: Typische Bautratenquarze (=> 
Artikelsammlung) haben krumme MHz-Werte.

>  UCSRC = (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
>
>  if (ZBits == 8) {
>    UCSRC |= (1<<UCSZ0);
>    UCSRC |= (1<<UCSZ1);
>  }

Disclaimer: Ich habe nicht nachgesehen, ob der Atmega32 das URSEL 
braucht. Es gibt AVR die brauchen es, andere brauchen es nicht. Ich 
vertraue darauf, dass ihr nachgesehen habt.

Beide Anweisungen zusammen ist IMHO witzlos und falsch. Beide 
Initialisierungen sind (sollen) für ZBits == 8 sein ((1 << UCSZ1) | (1 
<< UCSZ0)). Mit dem Unterschied, dass bei dem If-Fall URSEL nicht 
berücksichtigt wird.

Der If-Fall überschreibt euch UBRR, d.h. die Baudrateneinstellung, weil 
sich UCSRC und UBRR eine SFR-Speicheradresse teilen und nur URSEL 
entscheidet ob entweder UBRR oder UCSRC beschrieben wird.

von David (Gast)


Lesenswert?

Stefan B. schrieb:
> Der Systemtakt ist zur Kompilezeit i.d.R. bekannt und wird üblicherweise
> als Makro benutzt (F_CPU).

Wusste nicht, dass es dafür extra ein Makro gibt. Unser Dozent möchte 
das Programm so "Kundenfreundlich wie möglich haben", d.h. der "Kunde" 
soll nur im Main Prog was ändern brauchen, deswegen der übergebene 
Systemtakt.
Ich versuch das gleich mal ohne.

Stefan B. schrieb:
>  UCSRC = (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
>
>  if (ZBits == 8) {
>    UCSRC |= (1<<UCSZ0);
>    UCSRC |= (1<<UCSZ1);
>  }

> Beide Anweisungen zusammen ist IMHO witzlos und falsch. Beide
> Initialisierungen sind (sollen) für ZBits == 8 sein ((1 << UCSZ1) | (1
> << UCSZ0)). Mit dem Unterschied, dass bei dem If-Fall URSEL nicht
> berücksichtigt wird.

Da hast du recht :D Habe etwas ausprobiert und dann ausversehen beides 
hier mit eingefügt. Ich habe es einmal mit dem oberen versucht und dann 
mit dem unteren, aber nicht beide zusammen. Habe das jeweilige andere 
dann auskommentiert. Sorry für das mißverständnis :S.

Was schlägst du denn vor wie man das regeln könnte. Wenn ich dich 
richtig verstehe eventuell in die If-Abfrage noch eine Nebenbedingung 
mit URSEL?

Der Hintergrund ist der, dass wir, wie oben beschrieben, das Programm 
Kundenfreundlich gestalten müssen. Der Kunde soll in der Main nur 
angeben die viele P-Bits er haben möchte, bzw wie viele Stoppbits.
Deshalb haben mein Laborpartner und ich uns für diese If-Abfragen 
entschieden. Weil die Registerbits ja unterschiedlich gesetzt bzw nicht 
gesetzt werden, sobald sich die Anzahl der Zeichenbits ändert. D.h. wir 
haben darunter noch 4 andere If-Abfragen für 5,6,7,9 Zeichenbits.

Stefan B. schrieb:
> Der If-Fall überschreibt euch UBRR, d.h. die Baudrateneinstellung, weil
> sich UCSRC und UBRR eine SFR-Speicheradresse teilen und nur URSEL
> entscheidet ob entweder UBRR oder UCSRC beschrieben wird.

Das habe ich leider auch schon gemerkt :S.
Der Dozent hat uns gesagt, wenn man in UCSRC das 7. Bit setzt, dann 
schreibt der Kontroller nur in das UCSRC-Register. Dachte wenn ich das 
dann mache, würde der Fall, der hier leider eintrifft, nicht eintreffen.
Also, wie oben gefragt, was tun?^^ Vielleicht könntest du mir eine 
passende Lösung posten, weil ich bin im Moment echt überfordert. Und der 
Dozent ist auch für keine Fragen zu haben :S Versteh mich bitte nich 
falsch ich will kein fertiges Programm, denn meins läuft ansonsten gut, 
es ist halt nur dieser eine Knackpunkt womit das ganze Prog hinfällig 
wird.
Danke schonmal für die Hilfe ;)

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Hier dann auch ein Auszug aus dem korrigierten Quellcode:

1
void usart_init(uint32_t Systemtakt, /* in Hz */
2
                uint32_t Baudrate, 
3
                uint8_t ZBits, 
4
                uint8_t SBit, 
5
                uint8_t PBit) 
6
{
7
  uint16_t Teiler = ((Systemtakt+Baudrate*8)/(Baudrate*16)) - 1;
8
  UCSRB = (1<<TXEN);
9
  UCSRC = (1<<URSEL) | (1 << UCSZ1) | (1 << UCSZ0); // Asynchron 8N1 
10
  UBRRH = (uint8_t) (Teiler >> 8);
11
  UBRRL = (uint8_t) Teiler & 0x00FF;

Wenn die Routine unbedingt "universell" sein soll, schreib in die Doku, 
welche Werte int ZBits, int SBit, int PBit annehmen können und ODERE die 
Werte direkt auf UCSRC drauf. Käse gegessen.

  UCSRC = (1<<URSEL) | ZBits | PBit | SBit; // Asynchron xxx

Wenn du dann den 8N1 Fall haben willst, musst du usart_init so aufrufen:

usart_init(F_CPU, 9600, ((1 << UCSZ1)|(1 << UCSZ0)), 0, 0)

Wenn das dem Kunden zu kryptisch ist, definiere ihm Makros

#define UART_8DATENBITS (((1 << UCSZ1)|(1 << UCSZ0)))
#define UART_NOPARITY   0
#define UART_1STOPBIT   0
usw.

usart_init(F_CPU, 9600, UART_8DATENBITS, UART_NOPARITY, UART_1STOPBIT)

Wenn du am Doku schreiben bist, schreib auch rein, dass nicht alle 
Kombinationen aus Systemtakt und Baudrate funktionieren.

In http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART ist 
dafür eine Serie von Makros, die warnen, wenn Kombinationen benutzt 
werden, die einen zu großen Baudratenfehler produzieren.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Wenn du unbedingt bei den if-Abfragen bleiben willst und mehrzeilige 
Anweisungen schreiben willst, musst du beim Setzen von Bits in UCSRC 
halt immer das URSEL Bit mitsetzen.

  if (ZBits == 8) {
    UCSRC |= (1<<URSEL) | (1<<UCSZ0);
    UCSRC |= (1<<URSEL) | (1<<UCSZ1);
  }

Damit das auch bei mehrmaligem Aufruf von usart_init funktioniert, 
müssen wegen |= die Bits in UCSRC anfangs gelöscht sein/werden. Beim 
Löschen aber schön die URSEL benutzen!

Selbst dann kann dieses Vorgehen scheitern. Eine Hardwareinitialisierung 
(jetzt mal UART unabhängig) ist komplexer als eine einfache Zuweisung 
von Bits. Es kann sein, dass bei der Hardwareinitialisierung mehrere 
Bits zusammen gesetzt werden müssen (siehe URSEL + andere Bits) und das 
kleckern einzelner Bits nicht wie gewünscht arbeitet. Näheres dazu 
müsste das Datenblatt hergeben.

von Stefan E. (sternst)


Lesenswert?

Stefan B. schrieb:
> Wenn du unbedingt bei den if-Abfragen bleiben willst und mehrzeilige
> Anweisungen schreiben willst, musst du beim Setzen von Bits in UCSRC
> halt immer das URSEL Bit mitsetzen.
>
>   if (ZBits == 8) {
>     UCSRC |= (1<<URSEL) | (1<<UCSZ0);
>     UCSRC |= (1<<URSEL) | (1<<UCSZ1);
>   }

Sorry, aber das ist Unsinn.
Du kannst in dem Register nicht mit |= einzelne Bits setzen, denn was 
beim |= gelesen und verodert wird, ist der Inhalt von UBRRH.

von David (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Stefan B. schrieb:
>> Wenn du unbedingt bei den if-Abfragen bleiben willst und mehrzeilige
>> Anweisungen schreiben willst, musst du beim Setzen von Bits in UCSRC
>> halt immer das URSEL Bit mitsetzen.
>>
>>   if (ZBits == 8) {
>>     UCSRC |= (1<<URSEL) | (1<<UCSZ0);
>>     UCSRC |= (1<<URSEL) | (1<<UCSZ1);
>>   }
>
> Sorry, aber das ist Unsinn.
> Du kannst in dem Register nicht mit |= einzelne Bits setzen, denn was
> beim |= gelesen und verodert wird, ist der Inhalt von UBRRH.

Habe das gerade mal ausporbiert. Bei mir funktioniert das nicht. Der 
ändert trotzdem das UBRRH Register :S... Wie kann ich das denn jetzt 
umgehen trotz If-Schleife?

von Jörg G. (joergderxte)


Lesenswert?

>Der ändert trotzdem das UBRRH Register
Im Simulator? Das  ist ein Simulator-'fehler' -> Schau nach den "known 
issues' in der Hilfe.
(Warum das auslesen da so kompliziert ist und deshalb der Simulator da 
nie 'repariert' wurde,  findest du im Datenblatt  unter "Accessing 
UBRRH/ UCSRC  Registers")

von David (Gast)


Lesenswert?

Jörg G. schrieb:
>>Der ändert trotzdem das UBRRH Register
> Im Simulator? Das  ist ein Simulator-'fehler' -> Schau nach den "known
> issues' in der Hilfe.
> (Warum das auslesen da so kompliziert ist und deshalb der Simulator da
> nie 'repariert' wurde,  findest du im Datenblatt  unter "Accessing
> UBRRH/ UCSRC  Registers")

nein nicht nur im Simu. Ich habe mal die Register UCSRC und UBRRH 
nacheinander durch die LEDs ausgeben lassen.
Wenn ich die Zuweisung MIT URSEL mache, ist in beiden Registern, in 
UCSRC als auch in UBRRH, nicht drin.

Wenn ich die Zuweisung OHNE URSEL mache, ist in beiden Register das 1. 
und 2. Bit gesetzt (--> durch 8N1 (UCSZ0,UCSZ1 werden gesetzt))

von Stefan B. (stefan) Benutzerseite


Lesenswert?

David schrieb:

> Habe das gerade mal ausporbiert. Bei mir funktioniert das nicht. Der
> ändert trotzdem das UBRRH Register :S... Wie kann ich das denn jetzt
> umgehen trotz If-Schleife?

Das ist doch einfach: Inhalt für UCSRC in einer lokalen Variable 
zusammenbauen z.B. in den If-Abfragen und *anschliessend komplett in 
einer Zuweisung* nach UCSRC schreiben (= statt |=).

Wenn du die lokale Variable static machst und der Kunde UCSRC nicht 
ausserhalb usart_init manipuliert, dann kannst du auch den Inhalt von 
UCSRC "auslesen": Aus der statischen Kopie eben. Das kann nützlich sein, 
wenn der Kunde mit hintereinander geschalteten usast_init Aufrufen nur 
Teile der Einstellung ändern will.

von Karl H. (kbuchegg)


Lesenswert?

David schrieb:

> nein nicht nur im Simu. Ich habe mal die Register UCSRC und UBRRH
> nacheinander durch die LEDs ausgeben lassen.
> Wenn ich die Zuweisung MIT URSEL mache, ist in beiden Registern, in
> UCSRC als auch in UBRRH, nicht drin.
>
> Wenn ich die Zuweisung OHNE URSEL mache, ist in beiden Register das 1.
> und 2. Bit gesetzt (--> durch 8N1 (UCSZ0,UCSZ1 werden gesetzt))


Hast du beim Auslesen der Register auch das hier beachtet?
1
19.10.2 Read Access
2
Doing a read access to the UBRRH or the UCSRC Register is a more
3
complex operation. However, in most applications, it is rarely
4
necessary to read any of these registers.
5
The read access is controlled by a timed sequence. Reading the I/O
6
location once returns the UBRRH Register contents. If the register
7
location was read in previous system clock cycle, reading the register
8
in the current clock cycle will return the UCSRC contents. Note that
9
the timed sequence for reading the UCSRC is an atomic operation.
10
Interrupts must therefore be controlled (for example by disabling
11
interrupts globally) during the read operation.

Die Chance stehen IMHO nicht schlecht, dass du in Wirklichkeit gar nicht 
UCRSC und UBRRH angesehen hast, sondern 2 mal dasselbe Register, nämlich 
UBRRH.

Fazit: Beschreibe sowohl UCSRC als auch UBRRH immer in einem Schritt. 
Mach keine Veroderungen oder sonstwas, denn das wird öfter als dir lieb 
ist schiefgehen.
Ist ja auch keine große Sache, wenn man sich einfach vorher in einer 
Variablen alle Bits sammelt, so dass man dann 1 Byte hat, welches in 
einem Rutsch nach UCSRC geschrieben wird. Viel Lärm um nichts.

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.