hallo liebe avr gemeinde!
nach längeren hier wieder ein scheinbar unlösbares problem.
möchte mit einer funktion, der die parameter baudrate, datenbits, parity
und anzahl stopbits übergeben wird, die initialisierung der
uart-schnittstelle durchführen.
das problem ist, wenn ich die paramter so aufrufe, geht es:
UCSRC = (1<<URSEL)|(1<<UPM1)|(1<<UCSZ1)|(1<<UCSZ0)|UCSRC;
diese variante aber geht aber nicht mehr - wieso nicht???
funktionsaufruf:
modbus_init(9600, 8 , 'E', 1);
funktion selbst:
void modbus_init(u32 baud, u08 data, u08 parity, u08 stop)
// UART Initialisierung
{
u16 bauddiv;
// RS485-RxTx-Pin als Ausgang festlegen
cbi(RS485_PORT,RS485_RXTX_PIN);
sbi(RS485_DDR,RS485_RXTX_PIN);
// Baudrate einstellen
bauddiv = ((F_CPU + baud * 8)/(baud * 16)-1); // calculate division
factor for requested baud rate, and set it
UBRRH = bauddiv >> 8;
UBRRL = bauddiv & 0xFF;
// enable RxD/TxD and interrupts
UCSRB = UCSRB|(1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN);
// 8-Bit, Parity even, 1 Stop-Bit
//UCSRC = (1<<URSEL)|(1<<UPM1)|(1<<UCSZ1)|(1<<UCSZ0)|UCSRC;
switch (data)
{
case 5: // 5 Datenbits
break;
case 6: // 6 Datenbits
UCSRC = (UCSRC|(1<<URSEL)|(1<<UCSZ0));
break;
case 7: // 7 Datenbits
UCSRC = (UCSRC|(1<<URSEL)|(1<<UCSZ1));
break;
case 8: // 8 Datenbits
UCSRC = (UCSRC|(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1));
break;
}
switch (parity)
{
case 'N': // Parity NONE
break;
case 'E': // Parity EVEN
UCSRC = (UCSRC|(1<<URSEL)|(1<<UPM1));
break;
case 'O': // Parity ODD
UCSRC = (UCSRC|(1<<URSEL)|(1<<UPM0)|(1<<UPM1));
break;
}
if(stop == 2)
UCSRC = (UCSRC|(1<<URSEL)|(1<<USBS));
modbus_rxtx(RX_MODE); // RS485 auf Rx
}
bitte um hilfe, um das problem zu lösen - danke!!!
grüße peter
Gewöhn dir doch einfach eine Schreibweise alá
UCSRB |= (1<<RXCIE) | (1<<TXCIE) | (1<<RXEN) | (1<<TXEN);
an.
Ansonsten einfach mal aus dem Datenblatt übernommen:
UBRRH = (unsigned char) (baudiv>>8);
UBRRL = (unsigned char) bauddiv;
Schreib' immer die Interupt-Enable flags als letztes - hier also erst
UCSRC dann UCSRB.
Setz' dir den Wert für dein UCSRC in ein lokalen Variablen zusammen, das
spart viele Speicherzugriffe.
Du brauchst kein Read-modify-Write (|=, &=), wenn du die alleinige
Kontrolle hast -lass das | UCSRx einfach weg.
Überlass' das Baud-rechnen dem Compiler - übergib der Funktion den
fertigen Wert für das UBRR (Es sei denn du willst beliebige werte
erlauben, so dass eine Tabelle nicht reicht).
hth, Jörg
Jörg G. wrote:
> Setz' dir den Wert für dein UCSRC in ein lokalen Variablen zusammen, das> spart viele Speicherzugriffe.
Was glaubst du macht der Compiler da raus wenn mans nicht in einer
Variablen zusammensetzt?
Imho liegt das Problem im Switch Konstrukt.
Behandel die Einstellungen einzeln, pro Switch nur eine Sache (Parity,
Datenbits, Stopbits, etc.)
hi tim!
mach es eh so, das ich mit jedem konstrukt der switch-anweisung die
notwendigen bits im UCS´RC-register setze.
der grund ist der, das beim hochstarten des controllers die
einstellungen von einem dip-switch übernommen werden sollen. das geht ja
auch alles, nur worin liegt jetzt der unterschieb zwischen der
einzeiligen schreibweise und der schwitch-variante. vielleicht bin ich
auch schon blind, das ich den fehler nicht finde - ich weiß es einfach
nicht!
gruß peter
Das Problem ist, dass die I/O-Register als volatile gelten, die muss
der Compiler jedes mal einlesen. Wir wissen aber, dass sich dieses
Register nicht von allein verändert, und können das deshalb anders
machen:
1
uint8_ttmp=0;
2
switch(data)
3
{
4
case5:// 5 Datenbits
5
;
6
break;
7
case6:// 6 Datenbits
8
tmp=(1<<UCSZ0);
9
break;
10
case7:// 7 Datenbits
11
tmp=(1<<UCSZ1);
12
break;
13
case8:// 8 Datenbits
14
tmp=(1<<UCSZ0)|(1<<UCSZ1);
15
break;
16
}
17
18
switch(parity)
19
{
20
case'N':// Parity NONE
21
break;
22
case'E':// Parity EVEN
23
tmp|=(1<<UPM1);
24
break;
25
case'O':// Parity ODD
26
tmp|=(1<<UPM0)|(1<<UPM1);
27
break;
28
}
29
30
if(stop==2)
31
tmp|=(1<<USBS);
32
tmp|=(1<<URSEL);
33
UCSRC=tmp;
34
//HIER:
35
UCSRB=...
Das switch-Konstrukt ist kein Problem, es ist nur zu umständlich,
wenn die USART-Einstellungen nicht durch einen Benutzer im laufenden
Programm geändert werden können. Für internen Gebrauch (Schließt
auto-detect ein) würde ich Bitmasken (evtl. auch Bitfelder) nehmen.
hth, Jörg
ps unbedingt mal den generierten Assembler-Code ansschauen (.lss oder
lst-Datei)
sieht für mich alles in Ordnung aus, wobei zugegeben in der
Einzelanweisungs Nummer für jedes Bit nach dem ersten ein SBI
"Mehraufwand" ist.
Die Variante mit der Hilfsvariablen ist das Gleiche wie ersten beiden
Zuweisungsarten, welche als immediate behandelt werden solange es
statisch bleibt, was aber beim einlesen mittels DIP-Switch hinfällig
wird.
Peter wrote:
> auch alles, nur worin liegt jetzt der unterschieb zwischen der> einzeiligen schreibweise und der schwitch-variante. vielleicht bin ich> auch schon blind, das ich den fehler nicht finde - ich weiß es einfach> nicht!>> gruß peter
Nö, das hab ich in dem Zusammenhang garnicht gemeint, es ging darum das
du UCSRC auf diese Weise nirgends initialisierst, du schleppst immer den
Kram mit der Schon drin steht.
Ich würde vor den Switch Konstrukten mal ein UCSRC = (1<<URSEL); machen
damit die Bits genullt werden. Oder eben so wie oben beschreiben.
Ein |= ist imho nur übersichtlicher, allerdings beim Erstzuweisen fehl
am Platz.
EDIT grade noch einen Fehler im oben von mir geposteten Quelltext
entdeckt:
case 5: // 5 Datenbits
-> UCSRC = (1<<URSEL);
break;
Sonst wird bei 5 Datenbits UCSRC nicht genullt;
hi tim!
es gehen beide varianten leider nicht (deine und die von jörg).
hab euch mal von tim seiner variante das compilerergebniss reingestellt.
kenn mich mit assemler so gut wie überhaupt nicht aus - leider!
>es gehen beide varianten leider nicht
Was soll denn das heißen?
-Was hast du gemacht?
-Was soll passieren?
-Was passiert stattdessen?
Warum postest du keinen gescheiten (=komipilierbaren, aber auf ein
Minimum gekürzten) Code?
hth, Jörg
hi jörg!
den code zu beginn kann man eh soweit compilieren.
war mein fehler - vor lauter auskommentieren hab ich auch die
timeoutfunktion für die frameerkennung ausgeschaltet.
was aber das UCSRC-reg. btrifft ist das schon etwas seltsam - deine
variante geht (die mit der variablen). die andere von tim noch immer
nicht - frag mich nicht warum, aber es ist leider so.
ich arbeite auf jeden fall mit deiner weiter - muß morgen fertig sein!
... und ein ganz großes danke für die erfolgreiche unterstützung
grüße peter
hat nicht wirklich eine auswirkung - hab es einmal davor und dann danach
im code platziert. beide reihenfolgen gehen, werd aber sicherheitshalber
deine variante einsetzen.
Jörg G. wrote:
> Tim hat wieder erst UCSRB und dann UCSRC gesetzt (bzw. die> Reihenfolge von dir kopiert)>> .
Also das ist absolut egal, wenn du es nicht glaubst, schau dir die
Codebeispiele im Datenblatt des ATmega32 an, Seite 146ff.
Sowohl im ASM als auch in C wird dort UCSRB vorher geschrieben, das ist
total egal. Die Aktivierung erfolgt eh erst mit dem abschließenden
sei().
Die einzige Besonderheit die man beachten muss, ist das URSEL Bit bei
jedem Schreiben in UCSRC zu setzen.
Folgendes Programm habe ich grade erfolgreich auf einem ATmega8
getestet: