Forum: Mikrocontroller und Digitale Elektronik Mega32 UART Übertragungsproblem


von Joschi K. (joschi2804)


Lesenswert?

Hallo,

mein Problem ist, wie der Titel schon aussagt, der UART. Ich möchte 
zurzeit entweder Buchstaben oder zahlen an den PC senden. Leider 
funktioniert es nicht. Ich benutze den ATMEGA32, das STK500, 
Entwicklungsumgebung ist das AVR Studio 4. Habe mir eine "Mini-USB to 
UART" vom Conrad gekauft.
http://www.conrad.at/ce/de/product/197326/MINI-USB-TO-UART-CONVERTER
Ich benutze einen externen Quarz(7,3728MHz). Die Fuses habe ich 
hoffentlich richtig gesetzt. Ich habe als Clock-Source "Ext. 
Crystal/Resonator High Freq.; Start-up time:16K CK+64ms" ausgewählt.

So zum Problem. Wenn ich nun im Hyperterminal (benutze als hyperterminal 
hTerm) die empfangen Zeichen anschaue, muss ich leider feststellen, dass 
immer falsche zeichen kommen, zB ich sende 22 und es kommt die zahl 27 
an.

Hier mein Code:
1
#include <avr/io.h>
2
#include <avr/delay.h>
3
4
#ifndef F_CPU
5
#define F_CPU 7372800UL      //Frequency: 7,3728 MHz
6
#endif
7
8
/*  ******************************  */
9
/*    function prototypes      */
10
/*  ******************************  */
11
12
void Port_Init();
13
void UART_Init(uint16_t aBaud,uint8_t aU2X);
14
void UART_Write(char aData);
15
16
17
void main(void)
18
{
19
  //Port_Init();
20
  UART_Init(9600,0);    //Init Uart: BaudRate 9600, no double transmission speed
21
    while(1)
22
    {
23
    
24
    UART_Write('7');
25
    
26
    //test: is frequency true or false    
27
    //PORTB^=0xFF;
28
    //_delay_ms(1000);
29
    }
30
}
31
32
33
void Port_Init()
34
{
35
  DDRB=0xFF;
36
  PORTB=0x00;  
37
}
38
39
void UART_Init(uint16_t aBaud,uint8_t aU2X)
40
{
41
  UBRRH=0;
42
  if(aU2X)
43
  {
44
    UCSRA|=0x02;  //use double Speed    
45
    UBRRL=((uint32_t)F_CPU/(8*aBaud))-1;  //Calculate BaudRate using U2X
46
  }
47
  else
48
  {
49
    UCSRA=0;
50
    UBRRL=((uint32_t)F_CPU/(16*aBaud))-1;  //Calculate BaudRate without U2X
51
  }
52
  
53
  UCSRB|=0x18;  //Enable Receiver and Transmitter
54
  UCSRC|=0x83;  //asynchronous mode, 8 data bits, 1 stop bit, no parity bit
55
  
56
}
57
58
void UART_Write(char aData)
59
{
60
  while (!(UCSRA & 0x20));  //Wait for empty transmit buffer
61
  UDR=aData;          //Write data into Buffer
62
}

ich hoffe ihr könnt den Fehler finden.


mfg Joschi

von Bernd M. (bernd_m)


Lesenswert?

Hi,

hast Du auf dem STK500 auch den Oszillator auf den Quarz konfiguriert?

Du kannst auch den RS232 spare connector vom STK500 und dann via COM in 
den PC gehen, wenn COM port vorhanden, um mal das MiniUSB Dingens als 
Fehlerquelle auszuschliessen.

Apropos MiniDingensfehler: hast Du geprüft, ob Du eine Kommunikation via 
Hyperterm und MiniDings herstellen kannst (TestLoop)?

Gruss,
Bernd

von cskulkw (Gast)


Lesenswert?

Hallo Joschi, ich glaube Du castest da falsch.

Joschi Kraxner schrieb:
> void UART_Init(uint16_t aBaud,uint8_t aU2X)
>
> {
>
>   UBRRH=0;
>
>   if(aU2X)
>
>   {
>
>     UCSRA|=0x02;  //use double Speed
>
>     UBRRL=((uint32_t)F_CPU/(8*aBaud))-1;  //Calculate BaudRate using U2X
>
>   }
>
>   else
>
>   {
>
>     UCSRA=0;
>
>     UBRRL=((uint32_t)F_CPU/(16*aBaud))-1;  //Calculate BaudRate without U2X
>
>   }
>
>
>
>   UCSRB|=0x18;  //Enable Receiver and Transmitter
>
>   UCSRC|=0x83;  //asynchronous mode, 8 data bits, 1 stop bit, no parity bit
>
>
>
> }

Das UBRRL ist ein 8-Bit Register. Du führst einen CAST auf F_CPU durch 
und teilst im Anschluß die 32 Bit-Variable durch eine 16 BIt-Variable. 
PCLINT würde dabei meckern.

Außerdem halte ich den CAST von F_CPU für überflüssig, weil F_CPU 
bereits ein unsigned long ist!

Du müßtest auch Warnings haben, wie calculation on different 
signedness... oder so.

besser wäre UBRRL = (uint8_t) ( F_CPU / (uint32_t)(16*aBaud)) - 1;

Ich benutze die Funktion:

void USART_SetCommBaudRate(unsigned int param_BaudRateToSet,
      unsigned long param_ProcessorClockRate)
{
  float varl_BaudRateRegister = (param_ProcessorClockRate / (16.0 * 
(float) param_BaudRateToSet));
  unsigned int varl_IntBRR = (unsigned int) varl_BaudRateRegister;
  float varl_Remainder = varl_BaudRateRegister - (float) varl_IntBRR;
  /* laut Formel einen abziehen und das im Integer. */
    if(varl_Remainder < 0.5)
    {
      varl_IntBRR --;
    }

    UBRRL = (unsigned int)  (varl_IntBRR & 0x00FF);
    UBRRH = (unsigned int) ((varl_IntBRR & 0xFF00) >> 8);
}

Die Fließkommazahlen sind entweder nur während der Laufzeit der Routine 
gültig. Der Aufruf wäre so:

USART_SetCommBaudRate(2400,16000000.0);

Bei liefert Sie die erwarteten Baudraten. Auch wenn die Funktion etwas 
umständlich geschrieben ist. Aber so konnte ich im Debugger die 
Berechnung nachvollziehen. Sie stimmt.

Viel Erfolg.

von Karl H. (kbuchegg)


Lesenswert?

cskulkw schrieb:

> Das UBRRL ist ein 8-Bit Register. Du führst einen CAST auf F_CPU durch
> und teilst im Anschluß die 32 Bit-Variable durch eine 16 BIt-Variable.
> PCLINT würde dabei meckern.

Mag sein, dass der meckert. Die ganze Operation ist aber eindeutig 
definiert. Die Division wird in 32 Bit gemacht.

>
> Außerdem halte ich den CAST von F_CPU für überflüssig, weil F_CPU
> bereits ein unsigned long ist!

Prinzipiell richtig. Schadet aber auch nichts. Und wenn man bei der 
Definition von F_CPU einen Fehler macht, dann sorgt der Cast wenigstens 
immer noch für eine 32 Bit unsigned Operation.
Ist ein wenig doppelt gemoppelt, aber bei Sicherheitsleinen schadet das 
nicht.


>
> Du müßtest auch Warnings haben, wie calculation on different
> signedness... oder so.

Wieso sollte er die haben?

>
> besser wäre UBRRL = (uint8_t) ( F_CPU / (uint32_t)(16*aBaud)) - 1;

Ist völlig gleichwertig.
Und zwar in allen Belangen.


Edit: stimmt nicht ganz. Dein Ergebnis wird zuerst auf 8 Bit reduziert 
und dann 1 abgezogen. Bei ihm läuft eine 32 Bit Subtraktion, wenn der 
Compiler nicht erkennt, dass er das runtercasten vorziehen kann, weil 
das Ergebnis dasselbe ist.
Arithmetisch sind die beiden Ergebnisse allerdings gleichwertig.


>
> Ich benutze die Funktion:
>
> void USART_SetCommBaudRate(unsigned int param_BaudRateToSet,
>       unsigned long param_ProcessorClockRate)
> {
>   float varl_BaudRateRegister = (param_ProcessorClockRate / (16.0 *
> (float) param_BaudRateToSet));


Ähm.
float?

Das ist jetzt aber nicht dein Ernst.

Seine Funktion ist soweit ok, auch wenn das hier

  UCSRB|=0x18;  //Enable Receiver and Transmitter
  UCSRC|=0x83;  //asynchronous mode, 8 data bits, 1 stop bit, no parity 
bit

die dümmste Schreibweise ist, die er finden konnte. Und ich werde das 
jetzt nicht in Bits aufdröseln um zu sehen, ob er sich da bei einer 
Bitposition verhaut hat.
Die Nichtberechnung von UBRRH ist auch nicht wirklich toll, bei seinen 
Zahlenwerten aber auch noch kein Problem.

Wenn die Übertragung grundsätzlich läuft, dann ist das schon mal ein 
gutes Zeichen, denn dann ist Kabel und dergleichen schon mal kein 
Problem mehr.
Kommen falsche Zeichen an, dann liegt das Problem praktisch immer daran, 
dass der µC mit einer anderen Frequenz taktet, als mit der gerechnet 
wurde.

von Karl H. (kbuchegg)


Lesenswert?

Zum Testen auch mal eine kleine Pause hinter die Ausgabe einfügen
1
void main(void)
2
{
3
  //Port_Init();
4
  UART_Init(9600,0);    //Init Uart: BaudRate 9600, no double transmission speed
5
  while(1)
6
  {
7
    
8
    UART_Write('7');
9
    _delay_ms(10);
10
  }
11
}

um dem Empfänger eine Chance zu geben, eindeutig auf einen zeichenanfang 
zu synchronisieren, falls er 'schräg in die Übertragung einsteigt'.

von Joschi K. (joschi2804)


Lesenswert?

hallo,

sorry das ich erst jetzt schreibe, habe nicht so schnell antworten 
erwartet.

@Bernd: also eine Kommunikation findet statt. Ich empfange ja Zeichen. 
nur die passen eben nicht. Ich habe mir eine eigene kleine Miniplatine 
gebaut, auf der die Grundschaltung mit externen Quarz schon drauf ist. 
jetzt muss ich immer nur den Atmega programmieren und steck den 
Controller dann in die Platine. Hab mir gedacht, dass ich es vielleicht 
bei späteren Projekten gebrauchen könnte. Also brauch ich nicht den 
Oszillator am STK500 konfigurieren, oder?

so zum Rest:
danke für die Funktion. Hab sie auch gleich ausprobiert. Also wenn ich 
jetzt zB. ein 'a' sende empfange ich ein a, doch danach ein X, dann 
wieder a und wieder X...
also es passt noch nicht ganz. Habe auch versucht ein delay einzubauen, 
doch dann kommt wieder ein anderes Zeichen.

Tut mir leid dass ich alles in hexadezimal schreib. Nur ich programmiere 
noch nicht so lange mit dem ATMEGA. In der Schule benutzen wir den 
LPC935 und die Prof. wollen alle, dass wir hexadezimal schreiben. Werde 
aber in den nächsten Tagen in z.B.: UCSRB|=(1<<REN) umändern. Ich glaub 
so ca. funktioniert es.

Ja das mit der Frequenz habe ich mir auch immer gedacht. Dann habe ich 
ein Licht im Sekundentakt blinken lassen (mit der delay funktion), und 
es hat auch im sekundentakt geblinkt. Genau kann ich es nicht 
nachschauen, habe kein Oszi daheim.

Habe auch oft gelesen, dass es Probleme mit der Spannungsversorgung 
geben kann, benutze aber ein Netzgerät, und die sollte ja schon ziemlich 
konstante Spannung liefern.

mfg Joschi

von Joschi K. (joschi2804)


Lesenswert?

so also ich habe meinen Fehler jetzt gefunden. Ich habe mich bei den 
bits wirklich verschaut. Habe mich im Datenblatt auch falsch abgeschaut. 
hab nicht 8 bit character size, sondern ein anderen wert ausgewählt.
Jetzt funktioniert alles. Benutze aber noch die Funktion von cskulkw - 
möchte auch danke sagen - , denn ich habe es mit meiner zurzeit noch 
nicht getestet.

Danke auch noch an alle anderen, dass ihr ziemlich schnell geantwortet 
habt.
@Karl Heinz Buchegger: Glaube jetzt auch wirklich, dass es besser wäre, 
eine einfachere Schreibweise zu nehmen :)

thread kann glaub ich geschlossen werden.

mfg Joschi

von cskulkw (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ähm.
>
> float?
>
>
>
> Das ist jetzt aber nicht dein Ernst.

Doch sehr verehrter Moderator Karl-Heinz. Ich mache das nicht erst seit 
gestern. Diesen Code habe ich mehr als einmal mittels Debugger 
überprüft.

Wo ist das Problem eine Initialisierungsfunktion mit float-Werten 
berechnen zu lassen, solange sie lokal definiert sind? Dann werden sie 
nur auf dem Stack angelegt und sind im Betriebsmodus GESCHICHTE !!!!

Aber Herr Moderator weiß das natürlich besser. Also, ich habe keine 
Ahnung welche Qualifikation Du hast, aber mach es, wie Du es für richtig 
hälst. Übrigens steht der uint32_t Cast genau vor der Konstanten  F_CPU. 
Dieser Cast endet am Divisor-Operator. Wie gesagt, frage PC-Lint! Ich 
habe diese Tool gehasst, aber leider hatte es immer wieder Recht.

Und wie man liest, scheint Joschi jetzt glücklich zu sein.

Frohes Schaffen noch ...

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.