mikrocontroller.net

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


Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kurz noch als ergänzung:

controller ist ein atmega32

compiler WinAVR-20071221

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht 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;

Autor: Jörg G. (joergderxte)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht 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.)

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg G. (joergderxte)
Datum:

Bewertung
0 lesenswert
nicht 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:
 uint8_t tmp = 0; 
switch (data)
  {
    case 5:            // 5 Datenbits
      ;
      break;
    case 6:            // 6 Datenbits
      tmp = (1<<UCSZ0);
      break;
    case 7:            // 7 Datenbits
      tmp = (1<<UCSZ1);
      break;
    case 8:            // 8 Datenbits
      tmp = (1<<UCSZ0)|(1<<UCSZ1);
      break;
  }

  switch (parity)
  {
    case 'N':            // Parity NONE
      break;
    case 'E':            // Parity EVEN
      tmp |= (1<<UPM1);
      break;
    case 'O':            // Parity ODD
      tmp |= (1<<UPM0)|(1<<UPM1);
      break;
  }

  if(stop == 2)
    tmp |= (1<<USBS);
  tmp |= (1<<URSEL);
  UCSRC = tmp;
//HIER:
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)

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Versuchs mal damit:
void modbus_init(u32 baud, u08 data, u08 parity, u08 stop){
// UART Initialisierung
 uint16_t ubrr = ((F_CPU+baud*8)/(16*baud))-1;

// Baudrate einstellen
 UBRRH = (uint8_t)(ubrr>>8);
 UBRRL = (uint8_t)(ubrr);

 UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<<TXCIE); 

 switch (data){
    case 5:            // 5 Datenbits      
      break;
    case 6:            // 6 Datenbits
      UCSRC = (1<<URSEL) | (1<<UCSZ0);
      break;
    case 7:            // 7 Datenbits
      UCSRC = (1<<URSEL) |  (1<<UCSZ1);
      break;
    case 8:            // 8 Datenbits
      UCSRC = (1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1);
      break;
  } 
  
  switch (parity){
    case 'N':            // Parity NONE
      break;
    case 'E':            // Parity EVEN
      UCSRC |= (1<<URSEL) | (1<<UPM1);
      break;
    case 'O':            // Parity ODD
      UCSRC |= (1<<URSEL) | (1<<UPM0) | (1<<UPM1);
      break;
  }

  if(stop == 2) UCSRC |= (1<<URSEL) | (1<<USBS);

  sei();
}

Autor: Tim T. (tim_taylor)
Datum:

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

Jop, grade mal gemacht:
        UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE); //Receiver Enable
+0000004D:   E988        LDI     R24,0x98         Load immediate
+0000004E:   B98A        OUT     0x0A,R24         Out to I/O location

oder

        UCSRB = 0b10011000;
+0000004D:   E988        LDI     R24,0x98         Load immediate
+0000004F:   B98A        OUT     0x0A,R24         Out to I/O location

oder

        UCSRB = (1<<RXEN); //Receiver Enable
+00000050:   E180        LDI     R24,0x10         Load immediate
+00000051:   B98A        OUT     0x0A,R24         Out to I/O location
        UCSRB |= (1<<TXEN); //Transmitter Enable
+00000052:   9A53        SBI     0x0A,3           Set bit in I/O register
        UCSRB |= (1<<RXCIE); //RX Complete Interrupt Enable
+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.

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht 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;

Autor: Peter (Gast)
Datum:

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

Autor: Jörg G. (joergderxte)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg G. (joergderxte)
Datum:

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

.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tim T. (tim_taylor)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>

volatile char daten;

void modbus_init(uint32_t baud, uint8_t data, char parity, uint8_t stop){
// UART Initialisierung
 uint16_t ubrr = ((F_CPU+baud*8)/(baud*16)-1);

// Baudrate einstellen
 UBRRH = (uint8_t)(ubrr>>8);
 UBRRL = (uint8_t)(ubrr);

 UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE);// | (1<<TXCIE); 

  switch (data){
    case 5:            // 5 Datenbits   
      UCSRC = (1<<URSEL);   
      break;
    case 6:            // 6 Datenbits
      UCSRC = (1<<URSEL) | (1<<UCSZ0);
      break;
    case 7:            // 7 Datenbits
      UCSRC = (1<<URSEL) |  (1<<UCSZ1);
      break;
    case 8:            // 8 Datenbits
      UCSRC = (1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1);
      break;
  } 
  
  switch (parity){
    case 'N':            // Parity NONE
      break;
    case 'E':            // Parity EVEN
      UCSRC |= (1<<URSEL) | (1<<UPM1);
      break;
    case 'O':            // Parity ODD
      UCSRC |= (1<<URSEL) | (1<<UPM0) | (1<<UPM1);
      break;
  }

  if(stop == 2) UCSRC |= (1<<URSEL) | (1<<USBS);

  sei();
}

void send_char(char data) {
 while (!(UCSRA & (1<<UDRE)));
 UDR = data;
}

void send_string(char *data) {
 while (*data) { /* so lange *data != '\0' also ungleich dem "String-Endezeichen" */
  send_char(*data);
  data++;
 };
}

ISR(USART_RXC_vect){
 daten = UDR;
 send_char(daten);
 send_string(" - wurde empfangen\n\r");
}

int main(void){
 modbus_init(9600,8,'N',1);
 while(1);
 return 0;
}

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?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.