Forum: Compiler & IDEs Worin liegt der Unterschied "UCSRC"


von Peter (Gast)


Lesenswert?

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

von Peter (Gast)


Lesenswert?

kurz noch als ergänzung:

controller ist ein atmega32

compiler WinAVR-20071221

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

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;

von Jörg G. (joergderxte)


Lesenswert?

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

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

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

von Peter (Gast)


Lesenswert?

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

von Jörg G. (joergderxte)


Lesenswert?

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_t tmp = 0; 
2
switch (data)
3
  {
4
    case 5:            // 5 Datenbits
5
      ;
6
      break;
7
    case 6:            // 6 Datenbits
8
      tmp = (1<<UCSZ0);
9
      break;
10
    case 7:            // 7 Datenbits
11
      tmp = (1<<UCSZ1);
12
      break;
13
    case 8:            // 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)

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Versuchs mal damit:
1
void modbus_init(u32 baud, u08 data, u08 parity, u08 stop){
2
// UART Initialisierung
3
 uint16_t ubrr = ((F_CPU+baud*8)/(16*baud))-1;
4
5
// Baudrate einstellen
6
 UBRRH = (uint8_t)(ubrr>>8);
7
 UBRRL = (uint8_t)(ubrr);
8
9
 UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<<TXCIE); 
10
11
 switch (data){
12
    case 5:            // 5 Datenbits      
13
      break;
14
    case 6:            // 6 Datenbits
15
      UCSRC = (1<<URSEL) | (1<<UCSZ0);
16
      break;
17
    case 7:            // 7 Datenbits
18
      UCSRC = (1<<URSEL) |  (1<<UCSZ1);
19
      break;
20
    case 8:            // 8 Datenbits
21
      UCSRC = (1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1);
22
      break;
23
  } 
24
  
25
  switch (parity){
26
    case 'N':            // Parity NONE
27
      break;
28
    case 'E':            // Parity EVEN
29
      UCSRC |= (1<<URSEL) | (1<<UPM1);
30
      break;
31
    case 'O':            // Parity ODD
32
      UCSRC |= (1<<URSEL) | (1<<UPM0) | (1<<UPM1);
33
      break;
34
  }
35
36
  if(stop == 2) UCSRC |= (1<<URSEL) | (1<<USBS);
37
38
  sei();
39
}

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Jörg G. wrote:
> ps unbedingt mal den generierten Assembler-Code ansschauen (.lss oder
> lst-Datei)

Jop, grade mal gemacht:
1
        UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE); //Receiver Enable
2
+0000004D:   E988        LDI     R24,0x98         Load immediate
3
+0000004E:   B98A        OUT     0x0A,R24         Out to I/O location
4
5
oder
6
7
        UCSRB = 0b10011000;
8
+0000004D:   E988        LDI     R24,0x98         Load immediate
9
+0000004F:   B98A        OUT     0x0A,R24         Out to I/O location
10
11
oder
12
13
        UCSRB = (1<<RXEN); //Receiver Enable
14
+00000050:   E180        LDI     R24,0x10         Load immediate
15
+00000051:   B98A        OUT     0x0A,R24         Out to I/O location
16
        UCSRB |= (1<<TXEN); //Transmitter Enable
17
+00000052:   9A53        SBI     0x0A,3           Set bit in I/O register
18
        UCSRB |= (1<<RXCIE); //RX Complete Interrupt Enable
19
+00000053:   9A57        SBI     0x0A,7           Set bit in I/O register
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.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

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;

von Peter (Gast)


Lesenswert?

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!
1
 46                 /* function modbus_rxtx size 10 (10) */
2
  47                 .LFE30:
3
  49                 .global  modbus_init
4
  51                 modbus_init:
5
  52                 .LFB11:
6
  53                 .LM9:
7
  54                 /* prologue: frame size=0 */
8
  55 0014 FF92          push r15
9
  56 0016 0F93          push r16
10
  57 0018 1F93          push r17
11
  58                 /* prologue end (size=3) */
12
  59                 .LVL1:
13
  60 001a 142F          mov r17,r20
14
  61 001c F22E          mov r15,r18
15
  62                 .LM10:
16
  63 001e 9298          cbi 50-0x20,2
17
  64                 .LM11:
18
  65 0020 8A9A          sbi 49-0x20,2
19
  66                 .LM12:
20
  67 0022 F3E0          ldi r31,3
21
  68 0024 660F        1:  lsl r22
22
  69 0026 771F          rol r23
23
  70 0028 881F          rol r24
24
  71 002a 991F          rol r25
25
  72 002c FA95          dec r31
26
  73 002e 01F4          brne 1b
27
  74                 .LVL2:
28
  75 0030 9B01          movw r18,r22
29
  76 0032 AC01          movw r20,r24
30
  77                 .LVL3:
31
  78 0034 220F          lsl r18
32
  79 0036 331F          rol r19
33
  80 0038 441F          rol r20
34
  81 003a 551F          rol r21
35
  82 003c 6050          subi r22,lo8(-(16000000))
36
  83 003e 7C4D          sbci r23,hi8(-(16000000))
37
  84 0040 8B40          sbci r24,hlo8(-(16000000))
38
  85 0042 9F4F          sbci r25,hhi8(-(16000000))
39
  86 0044 0E94 0000     call __udivmodsi4
40
  87                 .LVL4:
41
  88 0048 2150          subi r18,lo8(-(-1))
42
  89 004a 3040          sbci r19,hi8(-(-1))
43
  90                 .LVL5:
44
  91                 .LM13:
45
  92 004c 832F          mov r24,r19
46
  93 004e 9927          clr r25
47
  94 0050 80BD          out 64-0x20,r24
48
  95                 .LM14:
49
  96 0052 29B9          out 41-0x20,r18
50
  97                 .LM15:
51
  98 0054 88ED          ldi r24,lo8(-40)
52
  99 0056 8AB9          out 42-0x20,r24
53
 100                 .LM16:
54
 101 0058 80E8          ldi r24,lo8(-128)
55
 102 005a 80BD          out 64-0x20,r24
56
 103                 .LM17:
57
 104 005c 1630          cpi r17,lo8(6)
58
 105 005e 01F0          breq .L9
59
 106                 .LVL6:
60
 107                 .LM18:
61
 108 0060 1730          cpi r17,lo8(7)
62
 109 0062 00F4          brsh .L12
63
 110 0064 1530          cpi r17,lo8(5)
64
 111 0066 01F4          brne .L7
65
 112 0068 00C0          rjmp .L19
66
 113                 .L12:
67
 114 006a 1730          cpi r17,lo8(7)
68
 115 006c 01F0          breq .L10
69
 116 006e 1830          cpi r17,lo8(8)
70
 117 0070 01F4          brne .L7
71
 118 0072 00C0          rjmp .L11
72
 119                 .L9:
73
 120                 .LM19:
74
 121 0074 82E8          ldi r24,lo8(-126)
75
 122 0076 00C0          rjmp .L19
76
 123                 .L10:
77
 124                 .LM20:
78
 125 0078 84E8          ldi r24,lo8(-124)
79
 126 007a 00C0          rjmp .L19
80
 127                 .L11:
81
 128                 .LM21:
82
 129 007c 86E8          ldi r24,lo8(-122)
83
 130                 .L19:
84
 131 007e 80BD          out 64-0x20,r24
85
 132                 .L7:
86
 133                 .LM22:
87
 134 0080 85E4          ldi r24,lo8(69)
88
 135 0082 F816          cp r15,r24
89
 136 0084 01F0          breq .L14
90
 137 0086 8FE4          ldi r24,lo8(79)
91
 138 0088 F816          cp r15,r24
92
 139 008a 01F4          brne .L13
93
 140 008c 00C0          rjmp .L15
94
 141                 .L14:
95
 142                 .LM23:
96
 143 008e 80B5          in r24,64-0x20
97
 144 0090 806A          ori r24,lo8(-96)
98
 145 0092 00C0          rjmp .L20
99
 146                 .L15:
100
 147                 .LM24:
101
 148 0094 80B5          in r24,64-0x20
102
 149 0096 806B          ori r24,lo8(-80)
103
 150                 .L20:
104
 151 0098 80BD          out 64-0x20,r24
105
 152                 .L13:
106
 153                 .LM25:
107
 154 009a 0230          cpi r16,lo8(2)
108
 155 009c 01F4          brne .L16
109
 156 009e 80B5          in r24,64-0x20
110
 157 00a0 8868          ori r24,lo8(-120)
111
 158 00a2 80BD          out 64-0x20,r24
112
 159                 .L16:
113
 160                 .LM26:
114
 161 00a4 80E0          ldi r24,lo8(0)
115
 162 00a6 0E94 0000     call modbus_rxtx
116
 163                 .LVL7:
117
 164                 /* epilogue: frame size=0 */
118
 165 00aa 1F91          pop r17
119
 166 00ac 0F91          pop r16
120
 167 00ae FF90          pop r15
121
 168 00b0 0895          ret
122
 169                 /* epilogue end (size=4) */
123
 170                 /* function modbus_init size 79 (72) */

von Jörg G. (joergderxte)


Lesenswert?

>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

von Peter (Gast)


Lesenswert?

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

von Jörg G. (joergderxte)


Lesenswert?

Tim hat wieder erst UCSRB und dann UCSRC gesetzt (bzw. die 
Reihenfolge von dir kopiert)

.

von Peter (Gast)


Lesenswert?

hat nicht wirklich eine auswirkung - hab es einmal davor und dann danach 
im code platziert. beide reihenfolgen gehen, werd aber sicherheitshalber 
deine variante einsetzen.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

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:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
5
volatile char daten;
6
7
void modbus_init(uint32_t baud, uint8_t data, char parity, uint8_t stop){
8
// UART Initialisierung
9
 uint16_t ubrr = ((F_CPU+baud*8)/(baud*16)-1);
10
11
// Baudrate einstellen
12
 UBRRH = (uint8_t)(ubrr>>8);
13
 UBRRL = (uint8_t)(ubrr);
14
15
 UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE);// | (1<<TXCIE); 
16
17
  switch (data){
18
    case 5:            // 5 Datenbits   
19
      UCSRC = (1<<URSEL);   
20
      break;
21
    case 6:            // 6 Datenbits
22
      UCSRC = (1<<URSEL) | (1<<UCSZ0);
23
      break;
24
    case 7:            // 7 Datenbits
25
      UCSRC = (1<<URSEL) |  (1<<UCSZ1);
26
      break;
27
    case 8:            // 8 Datenbits
28
      UCSRC = (1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1);
29
      break;
30
  } 
31
  
32
  switch (parity){
33
    case 'N':            // Parity NONE
34
      break;
35
    case 'E':            // Parity EVEN
36
      UCSRC |= (1<<URSEL) | (1<<UPM1);
37
      break;
38
    case 'O':            // Parity ODD
39
      UCSRC |= (1<<URSEL) | (1<<UPM0) | (1<<UPM1);
40
      break;
41
  }
42
43
  if(stop == 2) UCSRC |= (1<<URSEL) | (1<<USBS);
44
45
  sei();
46
}
47
48
void send_char(char data) {
49
 while (!(UCSRA & (1<<UDRE)));
50
 UDR = data;
51
}
52
53
void send_string(char *data) {
54
 while (*data) { /* so lange *data != '\0' also ungleich dem "String-Endezeichen" */
55
  send_char(*data);
56
  data++;
57
 };
58
}
59
60
ISR(USART_RXC_vect){
61
 daten = UDR;
62
 send_char(daten);
63
 send_string(" - wurde empfangen\n\r");
64
}
65
66
int main(void){
67
 modbus_init(9600,8,'N',1);
68
 while(1);
69
 return 0;
70
}

Kann es einfach sein das du die USART_TXC_vect ISR vergessen hast und 
darum bei einem UCSRB |= (1<<TXCIE); auf die Nase fällst?

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.