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_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)
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 | }
|
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.
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!
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) */ |
>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
Tim hat wieder erst UCSRB und dann UCSRC gesetzt (bzw. die Reihenfolge von dir kopiert) .
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:
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.