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); }
> 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.
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 ;)
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.
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.
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.
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?
>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")
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))
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.