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