Forum: Mikrocontroller und Digitale Elektronik Problem beim Empfang mit ATMEGA8 über UART


von Jan P. (jiss)


Lesenswert?

Hallo,

ich bin dabei die Kommunikation über RS232 über den Standard mit 
Baudrate=9600 und 8N1 aufzubauen.
Alle Daten, die ich vom µC aus sende kann ich in HTerm am PC empfangen. 
Sende ich jedoch vom PC aus etwas an den µC, so kommt dort nichts an.
Ich habe festgestellt, dass der Pegel von RXD am Atmega8 dauerhaft bei 
3,53V liegt (mit Digital-Multimeter), egal ob ich was sende oder nicht 
(das verwirrt mich)

Meine Schaltung habe ich wie im Tutorial aufgebaut:
http://www.mikrocontroller.net/wikifiles/e/ee/AVR-RS232.png
1
// Definition Taktfrequenz CPU
2
#ifndef F_CPU
3
#warning "F_CPU war noch nicht definiert, wird nun mit 3686400 definiert"
4
#define F_CPU 3686400UL                     // Quarz mit 3.6864 Mhz
5
#endif
6
7
// Baudrate für RS232 definieren
8
#define BAUD 9600UL                     // Baudrate
9
// Berechnungen
10
#define UBRR_VAL   ((F_CPU+BAUD*8)/(BAUD*16)-1)         // clever runden (=23,5 -> 23 -> 0x17)
11
#define BAUD_REAL   (F_CPU/(16*(UBRR_VAL+1)))           // Reale Baudrate (=9600)
12
#define BAUD_ERROR   ((BAUD_REAL*1000)/BAUD)         // Fehler in Promille, 1000 = kein Fehler.(=1000)
13
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
14
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! DDRC 0 20
15
#endif 
16
17
// Definitionen Konstanten
18
#define STR_LEN  80
19
20
// Einbinden der Header-Dateien
21
#include <util/delay.h> 
22
#include <avr/io.h> 
23
#include <stdlib.h>
24
#include <string.h>
25
#include <math.h>
26
#include <stdio.h>
27
#include <avr/interrupt.h> 
28
29
volatile uint8_t uart_str_complete = 0;     // 1 .. String komplett empfangen
30
volatile uint8_t uart_str_count = 0;
31
volatile char uart_string[UART_MAXSTRLEN + 1] = "";
32
volatile unsigned char receive_char;
33
34
ISR(USART_RXC_vect)
35
{
36
  receive_char = UDR;  
37
  uart_str_complete = 1;
38
}
39
40
// Initialisierung der USART-Schnittstelle
41
void uart_init(void)
42
{
43
  UBRRH = UBRR_VAL >> 8;                  // Baud Rate Register High
44
  UBRRL = UBRR_VAL & 0xFF;                // Baud Rate Register Low
45
  UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE);        // Setze UCSZ2 für 8N1
46
  UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);      // Selektiere UCSRC-Register, Asynchron 8N1
47
48
}
49
50
int uart_putc(unsigned char c)
51
{
52
    while (!(UCSRA & (1<<UDRE)))  /* warten bis Senden moeglich */
53
    {
54
    }                             
55
 
56
    UDR = c;                      /* sende Zeichen */
57
    return 0;
58
}
59
 
60
 
61
/* puts ist unabhaengig vom Controllertyp */
62
void uart_puts (char *s)
63
{
64
    while (*s)
65
    {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
66
        uart_putc(*s);
67
        s++;
68
    }
69
}
70
71
72
/* Zeichen empfangen */
73
uint8_t uart_getc(void)
74
{
75
    while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
76
    {
77
    PORTB ^= (1<<PB0);
78
    UDR=UCSRA;
79
    _delay_ms(500);
80
  }
81
    PORTB ^= (1<<PB1);
82
  UDR=UCSRA;
83
  _delay_ms(1000);
84
  return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben
85
}
86
 
87
void uart_gets( char* Buffer, uint8_t MaxLen )
88
{
89
  uint8_t NextChar;
90
  uint8_t StringLen = 0;
91
 
92
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen
93
 
94
                                  // Sammle solange Zeichen, bis:
95
                                  // * entweder das String Ende Zeichen kam
96
                                  // * oder das aufnehmende Array voll ist
97
  while( NextChar != '\n' && StringLen < MaxLen - 1 ) {
98
    *Buffer++ = NextChar;
99
    StringLen++;
100
    NextChar = uart_getc();
101
  }
102
 
103
                                  // Noch ein '\0' anhängen um einen Standard
104
                                  // C-String daraus zu machen
105
  *Buffer = '\0';
106
}
107
108
109
110
// #######################
111
// #### MAIN-FUNCTION ####
112
// #######################
113
int main(void)
114
{
115
  DDRB = 0xFF;
116
  PORTB |= (1<<PB0);
117
  uart_init();
118
  sei();
119
120
  while (1)
121
  {
122
    if (uart_str_complete)
123
    {
124
      PORTB ^= (1<<PB0);
125
    }
126
    else
127
    {
128
      PORTB ^= (1<<PB1);
129
      uart_putc(UCSRA);
130
    }
131
    uart_str_complete = 0;
132
  }  
133
   
134
  return 0;
135
}

Ich verwende einen Quarz mit 3,6864MHz und bin nun schon langsam 
verzweifelt, da ich schon seit 4 Tagen an dem Problem sitze und nicht 
voran komme.
Ein Loop durch Brücken der Receive- und Transferpins auf der 5V Seite 
des MAX232 funktioniert, sodass die Zeichen, die ich vom PC aus sende 
auch direkt wieder zurückkommen.
GND vom RS232-USB-Wandler an Pin 5 ist mit dem GND der 
Spannungsversorgung für den ATmega8 und dem MAX232 verbunden.

von Erik (Gast)


Lesenswert?

Du schreibst folgenden Befehl:

DDRB = 0xFF;

und bügelst damit über den PORT B.
Wolltest du nicht auch empfangen?

von spess53 (Gast)


Lesenswert?

Hi

>und bügelst damit über den PORT B. Wolltest du nicht auch empfangen?

PortB hat nichts mit der UART zu tun. Und selbst wenn, die UART 
überbügelt die Porteinstellungen.

MfG Spess

von Erik (Gast)


Lesenswert?

Naja aber die Datenrichtung am Port ist schon entscheidend und ein 
Ausgangsport ist nunmal ein Ausgangsport. Woher kommen denn sonst die 
3,5xxx Volt am Ausgang? Das hört sich für mich wie logisch high an.

von spess53 (Gast)


Lesenswert?

Hi

>Naja aber die Datenrichtung am Port ist schon entscheidend und ein
>Ausgangsport ist nunmal ein Ausgangsport. Woher kommen denn sonst die
>3,5xxx Volt am Ausgang? Das hört sich für mich wie logisch high an.

Sieh dir mal 'Alternate Port Functions' im Datenblatt an.

MfG Spess

von Erik (Gast)


Lesenswert?

Hi

Ja ich weiß das die alternative Funktion die Portfunktion überschreibt. 
An welchem Port liegt der UART? Dachte jetzt das es der B Port ist, naja 
trotzdem würde ich die Datenrichtung festlegen.

Versuch macht kluch.

Grüße

von Jan P. (jiss)


Lesenswert?

Der UART liegt beim ATmega8 am Port D an. Da ich TXEN und RXEN 
einschalte im Register, muss ich doch dort keine Datenrichtung angeben, 
da diese beiden Pins dann für den UART "reserviert" sind und nicht mehr 
als digitale Ports genutzt werden können.

von cskulkw (Gast)


Lesenswert?

Hallo Jörg,


1. Die Define Berechnungen sind schick. Aber trage doch erst einmal 17 
hex in das UBBRL und UBBRH 0hex ein. Das sind die harten Werte lt. 
Datenblatt. Damit schließt Du aus, dass die Formel keinen Fehler hat. 
Ansonsten hätte ich gesagt, Debugger dran und Register prüfen . Das geht 
aber bei mega8 nicht. Er hat kein OCDS.

2. Laß eine LED ggf. eine weitere blitzen, wenn der Interrupt aufgerufen 
wird. Dann weißt Du, dass zumindest der Empfang funktioniert.

3. Welche Sinn macht das Warten von 500 ms und weiteren 1000 ms in der 
uart_putc(). Ist das eine Debugging-Verzweifelungstat?

4. in uart_putc() fragst Du in "while (!(UCSRA & (1<<RXC)))". Der 
Empfangsinterupt setzt RXC automatisch zurück. Es wäre dann sinnvoller 
hier auf uart_str_complete zu testen.

ISR(USART_RXC_vect)
{
  receive_char = UDR;
  uart_str_complete = 1; <- möglicherweise eine Semaphore, obwohl hier 
kein End of Line Test  (0x0D, 0x0A)
}

wobei ich glaube, dass Du damit einen anderen Zweck verfolgt hast.

Versuche doch mal

if(ptr < UART_MAXSTRLEN +1)
    uart_string[ptr++];

in der Interruptroutine und lass diese uart_str_complete da weg. Die 
würde dort nur anzeigen, dass ein Byte empfangen wurde. Also so etwas 
wie ein RXC-Flag-Ersatz.

In get_char checkst Du nur noch, ob sich der "ptr" gegebenüber dem 
letzten Aufruf verändert hat.

if(last_ptr != ptr)
{
  "Mach was mit dem uart_string[ptr-1]" -> ptr wäre in der ISR bereits 
inkrementiert und zeigt auf nächsten beschreibbaren "leeren" 
Charakterplatz.

  /* Lastpointer updaten*/
  last_ptr = ptr; // wichtig, damit nur ein neuer Empfang berücksichtigt 
wird
}

5.
Die 3,5 Volt sind merkwürdig. Mach folgendes: zieh den RS232-stecker aus 
der Buchse, entferne den mega8 -wenn möglich oder unterbreche die 
Leitung zum MAX- und gebe auf den RX-Input vom MAX 0V drauf und miß am 
Ausgang. Der muß dann ca 4,5 Volt haben. Dann gebe 5Volt auf den 
RX-Input und der Ausgang muß ca. 0 Volt haben. Andernfalls ist der 
Schaltung etwas nicht in Ordnung. Dieser merkwürdige Wert kommt meist zu 
stande wenn der mega8 als Ausgang geschaltet ist und ein anderer Chip 
auch im Ausgangsmode ist. Wenn beide dann gegensätzlichen Pegel haben, 
kommt aufgrund der Strombegrenzung meist so ein merkwürdiges Ergebnis 
dabei heraus.

Eventuell ist der mega hin.

6.

DDRB = 0xFF sollte den Port D, an dem die UART hängt, nicht 
beeinflussen.

Vielleicht ist ja was dabei.

Viel Erfolg

von Jan P. (jiss)


Lesenswert?

Ich hab eine seltsame Feststellung gemacht, nachdem ich nun immer mehr 
davon ausgehe, dass das Problem auf der elektrischen Seite liegt.

Wenn ich die ISR's für RXD und TXD einprogrammiere und am Ausgang des 
PortB entsprechend die Pins 0 und 1 toggeln lasse, um mir die Aktivität 
anzuzeigen, dann funktioniert dies für ein Zeichen dass ich beim 
Mikrocontroller auf sich selbst sende (dabei sind RXD und TXD am µC 
gebrückt).
1
ISR(USART_TXC_vect)
2
{
3
  PORTB ^= (1<<PB1);
4
  _delay_ms(500);
5
}
6
7
ISR(USART_RXC_vect)
8
{
9
  unsigned char nextChar;
10
 
11
  // Daten aus dem Puffer lesen
12
  nextChar = UDR;
13
  PORTB ^= (1<<PB0);
14
  _delay_ms(500);
15
}
16
17
int main(void)
18
{
19
  DDRB = 0xFF;                       // Port B = Ausgang
20
  PORTB |= (1<<PB0);                    // Pin B0 aktivieren
21
  uart_init();                        // UART initialisieren
22
    char Line[40];      // String mit maximal 39 zeichen
23
  char z[5]={"A\n"};
24
  sei();
25
  while (1)
26
  {
27
    
28
    uart_put_string(z);
29
    //_delay_ms(500);
30
    //uart_gets(Line,sizeof(Line));
31
  }
32
  
33
  return 0;
34
}

Wenn ich nun RXD und TXD über den MAX232 schicke und RXD und TXD an der 
RS232-Seite des MAX232 brücke (in meinem fall die Pins 13 und 14), dann 
kommt keine Kommunikation im µC zustande (LEDs blinken nicht).

Folgenden Aufbau habe ich mit einem MAX232CPE
RXD vom µC -> an Pin 12 des MAX232 (R1OUT)
TXD vom µC -> an Pin 11 des MAX232 (T1IN)
Pin 13 (R1IN) und 14 (T1OUT) am MAX232 sind kurzgeschlossen/gebrückt

Habe ich einen Denkfehler? Meiner Meinung nach ist es nichts anderes als 
das direkte Brücken am µC, nur dass ich hier eine Wandlung des 
gesendeten Signals auf RS232-Pegel bringe und dieses anschließend hinter 
der Brücke wieder auf TTL-Pegel wandle und es auf den Empfangs-Pin des 
µC gebe.

Ich habe bereits 3 MAX232CPE vom gleichen Typ ausprobiert, um ein 
defektes Bauteil auszuschließen. Ebenfalls habe ich auch einen anderen 
ATmega8-µC in die Schaltung gesteckt.

Die Funktion des MAX232CPE habe ich auch mal zwischen 2 PCs getestet. 
Dabei bin ich bei der Kommunikation zwischen den beiden PCs über die 
RS232-TTL-Wanldung und TTL-RS232-Wandlung gegangen. Dies hat alles ohne 
Probleme funktioniert.

von Jan P. (jiss)


Lesenswert?

Hab auch gerade den Eingang R1IN getestet und geschaut was an dessen 
Ausgang R1OUT ansteht.
0V rein (R1IN) -> 5V raus (R1OUT)
5V rein (R1IN)-> 0V raus (R1OUT)
Passt also alles, so wie es soll.

Andersrum ebenfalls.
0V rein (T1IN) -> 9V raus (T1OUT)
5V rein (T1IN) -> -9V raus (T1OUT)

von cskulkw (Gast)


Lesenswert?

Moin Jörg,

sag Du hast immer noch diese Delay-Funktionen drin! Warum?

Schickst Du VOM Terminal Einzelzeichen?

Du toggelst doch die LEDs, eine Zeitverzögerung macht da wirklich keinen 
Sinn.

Woher weiß Deine Funktion

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

das der String zu ende ist? In uart_putc wird nicht auf auf *c == '\0' 
geprüft.

Nur falls Du Dich wunderst, dass im HT kein A - neue Zeile erscheint.

Warum weist Du in uart_getc   UDR=UCSRA;   dem Datenregister das 
Statusregister A zu? Da wird keine Zeichen im HT zu lesen sein, weil es 
u. U. ein Steuerzeichen sein kann.

Hast Du nur ein Multimeter? Kein Oszilloskop? Kannst Du Dir keins 
ausleihen? Messtechnik muß sein. Ansonsten wirst Du weiterhin im Nebel 
stochern.

von Jan P. (jiss)


Lesenswert?

cskulkw schrieb:
> Moin Jörg,
>
> sag Du hast immer noch diese Delay-Funktionen drin! Warum?

Die Delay ist dort drin, um bei HTerm noch mitverfolgen zu können, wann 
sich das Statusregister UCSRA ändert. Das ist auch der Grund, weshalb 
ich mir mit UDR=UCSRA das Statusregister ausgeben lasse, um es in HTerm 
zu sehen.

>
> Schickst Du VOM Terminal Einzelzeichen?

ja das tue ich, da ich überprüfen möchte, wie sich das Statusregister A 
verändert. Es scheint so, als ob kein gesendetes Zeichen beim µC 
ankommt.

>
> Du toggelst doch die LEDs, eine Zeitverzögerung macht da wirklich keinen
> Sinn.
Doch, da die LEDs sonst so schnell Ein- und Ausschalten, dass ich davon 
an der LED nichts sehe, die diese dauerhaft leuchten würde. So kann ich 
das E/A der LED sehen.
>
> Woher weiß Deine Funktion
>
> void uart_puts (char *s)
> {
>     while (*s)
>     {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
>         uart_putc(*s);
>         s++;
>     }
> }
>
> das der String zu ende ist? In uart_putc wird nicht auf auf *c == '\0'
> geprüft.

Die WHILE-Schleife ist solange erfüllt, solange die Bedingung (*s) nicht 
NULL wird. Das Zeichen NULL entspricht ja 0x00 bzw. dem 
Stringe-Ende-Zeichen \0

>
> Nur falls Du Dich wunderst, dass im HT kein A - neue Zeile erscheint.

In HTerm kann ich einstellen, dass bei der binären Null der Cursor in 
die neue Zeile gesetzt wird.

>
> Warum weist Du in uart_getc   UDR=UCSRA;   dem Datenregister das
> Statusregister A zu? Da wird keine Zeichen im HT zu lesen sein, weil es
> u. U. ein Steuerzeichen sein kann.

Das stimmt. Wenn ich mir das empfangene Zeichen jedoch binär darstellen 
lasse, dann sehe ich den Inhalt des UCSRA

>
> Hast Du nur ein Multimeter? Kein Oszilloskop? Kannst Du Dir keins
> ausleihen? Messtechnik muß sein. Ansonsten wirst Du weiterhin im Nebel
> stochern.
Das ist leider nicht so einfach. Ich besitze zwar ein altes Oszi, 
allerdings ohne Triggerfunktion

von cskulkw (Gast)


Lesenswert?

Nun, aber faktisch hast Du Dein Ziel immer noch nicht erreicht, oder?

Als ich vor Jahren mit der UART beim C51 angefangen hatte, gab es noch 
kein OnChip Debugging. Also habe ich ein Zeichen permanent gesendet und 
dann mit dem Skope nachgemessen. Dann braucht man nur auf eine steigende 
oder fallende Flanke triggern. Das sollte jedes noch so alte Oszilloskop 
können. Ansonsten wäre es nutzlos.

So, jetzt zu Deinem Problem.

Was kannst Du machen. Schließe 8 LED in der Reihenfolge an, dass man 
damit die Bits 0 bis 7 ablesen kann. Klemme sie zunächst an den Port B 
und schalte den auf Ausgang. Dann weist Du den Inhalt des 
UDR-Empfangsregister dem Port zu.

PortB = nextChar;

Möglicherweise kann der SPI mit gesetzter Fuse (weil über den 
programmiert wird) dazwischen funken.

Dann machst Du folgendes:

DDRC = 0x3F;
DDRD |= 0xC0;

PORTC = nextChar; (Bits 0 bis 5 UDR)
PORTD = (nextChar & 0xC0); (Bits 6 und 7 des UDR)

Jetzt hättest Du die Möglichkeit die Registerinhalte in Kombination mit 
der Warteschleife zu überprüfen.

Prinzipiell kannst Du Dir daraus auch eine Funktion basteln und dann 
damit auch das UDSRA anguggen.


Ich habe mir eine Schaltbox gebaut, die über RS232 ein Relais ein und 
ausschaltet. Dabei sollte sich der mega8 wie ein Töllner-Netzteil 
verhalten. Auch mich hatte das Ding Stunden gekostet, weil man nichts 
sieht. Die Fehler habe ich letztlich auf einem mega16 mit Debugger 
gefunden. Erst als es da funktionierte, übertrug ich den Code.

Du hast Dir einen kostengünstigen aber steinigen Weg ausgesucht.

von Jan P. (jiss)


Lesenswert?

Das ist genau das Problem, das ich habe. Ich sehe bei dem ATmega8 gar 
nichts.
Gibt es denn Chips, bei denen ich ein On-Chip-Debugging machen kann?
Was würdest du mir als Einsteiger dabei empfehlen?
Ich verballere hier inzwischen schon Tage und komme nicht voran.

von spess53 (Gast)


Lesenswert?

Hi

>Gibt es denn Chips, bei denen ich ein On-Chip-Debugging machen kann?
>Was würdest du mir als Einsteiger dabei empfehlen?

Ja. ATMega88. Dazu einen AVR-Dragon.

MfG Spess

von cskulkw (Gast)


Lesenswert?

Hallo Jörg,

einfach bei Ebay den Begriff "JTAGICE" eingeben. Dann findest Du jede 
Menge Nachbauten für 20 - 30 Euro. Ich habe auch mit so einem Dongle 
angefangen. Die sind nicht schnell, aber wirksam. Du kannst das Teil 
dann aus dem AVR-Studio heraus bedienen.

Der AVRDragon kostet neu zw. 50 - 60 Euro. Kann aber auch mehr.

Das AVR JTAGICE mkII ist so die Mittelklasse. Der kostet aber auch schon 
260 - 300 Euro. Der wird wohl eher nicht für Dich in Frage kommen.

Wenn Du mit dem ATmega16 kein Problem hast, dann nimm doch den.

Gruß
cskulkw

von Maik (Gast)


Lesenswert?

Hallo,

ich hab eingentlich genau dasselbe Problem wie oben beschrieben, nur 
nutze ich einen ATmega32 und möchte per UART-Interrupt Daten empfangen.

Mit normaler if((UCSRA & (1<<RXC)))-Abfrage in meiner main.c klappt das 
alles prima.

Für den Interruptbetrieb habe ich die Routine genommen, die im 
AVR-gcc-Totorial hier steht.

Allerdings springt mein Programm nicht in die UART-ISR,wenn ich über 
HTerm ein zeichen sende.

Weiß aber grad nicht warum! RXCIE-Bit in UCSRB habe ich gesetzt.

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.