Forum: Mikrocontroller und Digitale Elektronik Serielle Übertragung


von Karsten S. (scottyrebel)


Lesenswert?

Hallo,
ich habe ein Problem mit der Übertragungüber die serielle Schnittstelle. 
Im Einsatz habe ich eine ATMega32 und 14.7456 MHz. Meine Verbindung zum 
PC geht über eine FT232(FTDI) auf USB. Angestrebt sind 115200 Baud bei 
asynchronen Betrieb. Hier erstmal meine Einstellungen im Programm:

USART_Init:
;Set baud rate
LDI     temp, 0x00
OUT     UBRRH, temp
LDI     temp, 0x0F ;Asynchron, 14.7456 MHz, 115200Baud
;LDI    temp, 0x07 ;Synchron, 14.7456 MHz, 115200Baud
OUT     UBRRL, temp

;Set to asynchonius mode
LDI     temp, 0x00
LDI     temp, (1<<U2X)
OUT     UCSRA, temp

;Enable receiver and transmitter
LDI     temp, 0x00
LDI     temp, (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN)
OUT     UCSRB, temp

;Protocol: 8,N,1
LDI     temp, 0x00
LDI     temp, (1<<URSEL)|(1<<USBS)|(1<<UCSZ1)|(1<<UCSZ0)
OUT     UCSRC, temp

Empfangen will ich via Interrupt, hier die Routine:

USART_RX_Complete:
PUSH  temp ;Save temporary Variable
IN   temp, SREG
PUSH  temp ;Save Status-Register

IN   received, UDR ;Byte from USART
OUT   PORTC, received

POP   temp ;Restore Status-Register
OUT   SREG, temp
POP   temp ;Restore temporary Variable

RETI

Soweit tut das auch, aber nicht immer. Wenn ich größere Blöcke zum µC 
sende, werden die Daten anscheinend nicht richtig empfangen. Schicke ich 
bspw. 2048 Bytes mit dem Wert 12, so sehe ich das andere Werte an den 
Port angelegt werden. Jedes 3-5 Mal liegt dann zum Ende ein Wert am Port 
C den ich gar nicht gesendet habe, bspw. 136 anstelle von 12.

Ich gehe mal davon aus, das meine Comport-Einstellungen im µC falsch 
sind, aber so habe ich das aus der Dokumentations von Atmel gelesen. Am 
PC sind die Einstellungen korrekt, das habe ich mit einer Hardware 
kontrolliert.

Ich hoffe Ihr könnt mir weiterhelfen.
--
Gruß Karsten

von Klaus 2. (klaus2m5)


Lesenswert?

Dein Kommentar sagt 8n1, Du setzt aber 8n2.

von Hc Z. (mizch)


Lesenswert?

Im gezeigten Code sehe ich kein Problem (was nicht heißt, dass es keines 
gibt).  Mit fehlt jedoch der ganze Code, z.B. um zu prüfen, dass PORTC 
nirgendwo anders auftaucht, das Register „received“ bzw. ein Alias davon 
nirgendwo anders verwendet wird und dass ein Interrupt-Handler für den 
Tx-Interrupt vorhanden ist.

von Hc Z. (mizch)


Lesenswert?

Dann wäre noch die Standard-Frage bei PORT C:  JTAG abgeschaltet (Fuse, 
MCUCSR)?

von Karsten S. (scottyrebel)


Lesenswert?

Klaus 2m5 schrieb:
> Dein Kommentar sagt 8n1, Du setzt aber 8n2.

Hallo Klaus,
habe jetzt

LDI     temp, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)
OUT     UCSRC, temp

gesetzt, dadurch ist das Problem aber nicht behoben, eher schlimmer 
geworden. Meine Einstellungen auf der PC Seite stehen defintiv auf 8, N, 
1.

--
Gruß Karsten

von Karsten S. (scottyrebel)


Lesenswert?

Hc Zimmerer schrieb:
> Im gezeigten Code sehe ich kein Problem (was nicht heißt, dass es keines
> gibt).  Mit fehlt jedoch der ganze Code, z.B. um zu prüfen, dass PORTC
> nirgendwo anders auftaucht,

Hallo,
nein PortC wird nur dort verwendet.

> das Register „received“ bzw. ein Alias davon
> nirgendwo anders verwendet wird

.def temp    = R16
.def received  = R18

die werden auch nicht anderweitig benutzt. Die Anwendung soll lediglich 
nur ein paar Bytes einlesen.

> und dass ein Interrupt-Handler für den
> Tx-Interrupt vorhanden ist.

Ist er nicht, aber sollte doch eigentlich kein Problem sein?

--
Gruß Karsten

von Hc Z. (mizch)


Lesenswert?

Zu den 2 Stopbits:  Da ist auch keine Änderung zu erwarten.  Der 
Empfänger ignoriert ein zweites Stopbit, auch wenn es eingestellt ist. 
Es wirkt sich nur auf den Sender aus.

von Karl H. (kbuchegg)


Lesenswert?

Karsten Sosna schrieb:
> Klaus 2m5 schrieb:
>> Dein Kommentar sagt 8n1, Du setzt aber 8n2.
>
> Hallo Klaus,
> habe jetzt
>
> LDI     temp, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)
> OUT     UCSRC, temp
>
> gesetzt, dadurch ist das Problem aber nicht behoben, eher schlimmer
> geworden. Meine Einstellungen auf der PC Seite stehen defintiv auf 8, N,
> 1.

Geh da mal auf 8 N 2
Der Sender darf immer mehr Stoppbits benutzen. Mehr Stoppbits bedeutet 
einfach nur, dass der Sender dem Empfänger ein bischen mehr Zeit 
einräumt um mit dem empfangenen Byte etwas zu tun. Dem Empfänger ist die 
Anzahl der Stoppbits normalerweise sowieso egal.

Kannst du senderseitig mal nach jedem Byte (oder x Bytes) eine Pause 
einlegen? Kurz und gut: Wenn du die Übertragungsgeschwindigkeit mal 
runterdrossest, bleiben dann die Fehler oder verschwinden sie?

von Karsten S. (scottyrebel)


Lesenswert?

Hc Zimmerer schrieb:
> Dann wäre noch die Standard-Frage bei PORT C:  JTAG abgeschaltet (Fuse,
> MCUCSR)?

Fuse Bits

High 0xD9
Low 0xFF

Damit sollte JTAGEN abgeschaltet sein.

--
Gruß Scotty

von Hc Z. (mizch)


Lesenswert?

Karsten Sosna schrieb:
>> und dass ein Interrupt-Handler für den
>> Tx-Interrupt vorhanden ist.
>
> Ist er nicht, aber sollte doch eigentlich kein Problem sein?

Nur solange Du sicher nicht nach UDR schreibst und selbst dann ist es 
kein besonders guter Stil, dem AVR einen Interrupt zu erlauben, für den 
es keine Routine gibt.  Das schreit nach späteren Problemen.

von Klaus 2. (klaus2m5)


Lesenswert?

Erstmal vielleicht eine niedrigere Baudrate versuchen.

Ungewöhnlich: UBRRL=0x0F, UX2=1
Besser:       UBRRL=0x07, UX2=0

Double-Speed verringert das Bit-Sampling von 16 auf 8 Samples, 
allerdings nur bei einer schlechten RS232-Verbindung ein Problem.

von Karsten S. (scottyrebel)


Lesenswert?

> Geh da mal auf 8 N 2
> Der Sender darf immer mehr Stoppbits benutzen. Mehr Stoppbits bedeutet
> einfach nur, dass der Sender dem Empfänger ein bischen mehr Zeit
> einräumt um mit dem empfangenen Byte etwas zu tun. Dem Empfänger ist die
> Anzahl der Stoppbits normalerweise sowieso egal.
>
> Kannst du senderseitig mal nach jedem Byte (oder x Bytes) eine Pause
> einlegen? Kurz und gut: Wenn du die Übertragungsgeschwindigkeit mal
> runterdrossest, bleiben dann die Fehler oder verschwinden sie?

Hallo Karl,
habe ich ich beides ausprobiert, es ändert sich nichts. ganz im 
Gegenteil, damit bekomme ich gar keine vernünftige Übertragung mehr hin. 
Bin jetzt bei 38400, 8, N, 2
--
Gruß Karsten

von Karl H. (kbuchegg)


Lesenswert?

Karsten Sosna schrieb:

 habe ich ich beides ausprobiert, es ändert sich nichts. ganz im
> Gegenteil, damit bekomme ich gar keine vernünftige Übertragung mehr hin.

Das schreit aber dann nach einem Timingproblem.

Runter mit der Baudrate auf 300, im Sender nach jedem Byte eine Pause 
einlegen (und den Bytewert hochzählen). Auf dem AVR das empfangene Byte 
auf einen Port ausgeben (oder sonst irgendwo wo man den Wert sieht) und 
beobachten: Ist die Anzahl der korrekt empfangenen Bytes immer gleich? 
Wieviele Bytes sind hintereinander fehlerhaft? Gibt es da Zusammenhänge?

von Karsten S. (scottyrebel)


Lesenswert?

Klaus 2m5 schrieb:
> Erstmal vielleicht eine niedrigere Baudrate versuchen.
>
> Ungewöhnlich: UBRRL=0x0F, UX2=1
> Besser:       UBRRL=0x07, UX2=0

Hallo Klaus,

die zweite Einstellung habe ich auch schon durch gehabt, brachte aber 
keine Änderung.

Ich gehe jetzt mal auf 9600, 8, N, 2 Synchron

Absolute Katastrophe, jetzt kann ich sehen wie unregelmäßig sich der 
Port ändert.
Ich sende einfach 2048 Bytes a Wert 12, das Ergenis ist ein 
Zufallszahlengenerator.

--
Gruß Scotty

von Hc Z. (mizch)


Lesenswert?

Ich habe zwar Zweifel, ob das der wirkliche  Grund ist.  Aber für einen 
externen Quarz von 14.7456 MHz müsste die CKOPT-Fuse 0 sein, nach Deinen 
Fuse-Angaben ist sie aber 1.  Meistens tut's trotzdem.

von Karsten S. (scottyrebel)


Lesenswert?

> Das schreit aber dann nach einem Timingproblem.

So, ich habe jetzt auf 9600 gekürzt, die Anzahl der empfangenen Bytes 
stimmt nie. Ich sende einfach 255 Bytes. Beim ersten Durchlauf sagt er 
0, beim zweiten 67 oder mal 11 oder mal 165. Da haut irgendwas nicht 
hin. Stimmen denn meine Fuse-Bits für externer Quartz 14.7465MHz?
--
Gruß Karsten

von Hc Z. (mizch)


Lesenswert?

Nein.  S.o. :)

von Karsten S. (scottyrebel)


Lesenswert?

Hc Zimmerer schrieb:
> Ich habe zwar Zweifel, ob das der wirkliche  Grund ist.  Aber für einen
> externen Quarz von 14.7456 MHz müsste die CKOPT-Fuse 0 sein, nach Deinen
> Fuse-Angaben ist sie aber 1.  Meistens tut's trotzdem.

Laut AVR-Studio ist sie 0.
--
Gruß Karsten

von Hc Z. (mizch)


Lesenswert?

Äm.  Schriebst Du nicht oben

Karsten Sosna schrieb:
> Fuse Bits
>
> High 0xD9
> Low 0xFF

Und CKOPT ist Bit 4 in der High Fuse?

Edit:  Du hast schon den ATmega32 eingestellt?
Edit2:  Und berücksichtigt, dass programmierte Fuses auf 0 sind?

von Karsten S. (scottyrebel)


Lesenswert?

>> High 0xD9
>> Low 0xFF
>
> Und CKOPT ist Bit 4 in der High Fuse?
>
> Edit:  Du hast schon den ATmega32 eingestellt?

So habe jetzt CKOPT "angehakt", damit ändert sich High von 0xD9 auf 
0xC9.

Ich habe aber noch was gemacht. Ich habe die Baudrate auf 57600 
festgelegt. Wenn ich mir dieser Rate sende, kommen unterschiedlich viel 
Bytes an. Gehe ich jetzt mit dem Sender runter auf bspw. 38400 oder 
19200 dann kommen exakt soviel Bytes an wie ich gesendet habe. Der µC 
steht aber weiterhin auf 57600.
Desweiteren funktioniert das erst nach dem ersten Datenblock.

--
Gruß Karsten

von Hc Z. (mizch)


Lesenswert?

Das wird immer ominöser, und den vollen Code haben wir auch nicht.  Dass 
bei abweichender Baudrate genau soviel Zeichen kommen wie gesendet 
wurden, ist doch seeeehr unwahrscheinlich.  Und das sogar bei 
verschiedenen Baudrates.

Lass' mal den Empfang Empfang sein.  Mache das Erlauben des 
Sendeinterrupts weg und danach im Hauptprogramm eine Sendeschleife. 
Also ganz einfach immer dann, wenn TxC gesetzt ist, z.B. 0x31 nach UDR 
schreiben, dann eine Warteschleife (damit der Empfänger nicht 
fehlsynchronisiert, also z.B. verschachtelt 2 Register runterzählen), 
und das in einer ewigen Schleife.

Dann schaust Du, ob bei übereinstimmenden Baudrates „11111111...“ 
empfangen wird.  Erst wenn das der Fall ist, nimmst Du die Arbeit am 
Empfänger wieder auf.

von Karsten S. (scottyrebel)


Lesenswert?

> Das wird immer ominöser, und den vollen Code haben wir auch nicht.  Dass
> bei abweichender Baudrate genau soviel Zeichen kommen wie gesendet
> wurden, ist doch seeeehr unwahrscheinlich.  Und das sogar bei
> verschiedenen Baudrates.

Hallo,
das ist mir ja auch schleierhaft.

> Lass' mal den Empfang Empfang sein.  Mache das Erlauben des
> Sendeinterrupts weg und danach im Hauptprogramm eine Sendeschleife.
> Also ganz einfach immer dann, wenn TxC gesetzt ist, z.B. 0x31 nach UDR
> schreiben, dann eine Warteschleife (damit der Empfänger nicht
> fehlsynchronisiert, also z.B. verschachtelt 2 Register runterzählen),
> und das in einer ewigen Schleife.
>
> Dann schaust Du, ob bei übereinstimmenden Baudrates „11111111...“
> empfangen wird.  Erst wenn das der Fall ist, nimmst Du die Arbeit am
> Empfänger wieder auf.

Dein Rat bin ich gefolgt, das Senden vom µC zum PC funktioniert 
einwandfrei, dabei ist es egal ob ich das mit 9600 Baud oder 115200 Baud 
probiere.
Werde jetzt mal probieren was passiert wenn ich beim Empfang nicht via 
Interrupt sondern Polling gehe. Hänge jetzt mal bestehenden Code an. 
Einige Passagen habe ich aus kommentiert, das es ja der Test für das 
Senden war.

\\\
.include "m32def.inc" ;Binding Definitionsfile for Processortype

.equ F_CPU = 14745600 ;Systemclock in Hz
.equ BAUD  = 115200   ;Baudrate

; Calculation
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)

.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))       ; max. +/-10 Promille 
error
  .error "SystemError: Baudrate-Error greater 1 Percent, thats to high!"
.endif

.def temp    = R16
.def received  = R18
.def counter1  = R20
.def counter2  = R21
.def counter3  = R22
.def count  = R23

.org 0x00
    RJMP Initialize ;Reset
.org URXCaddr
    RJMP USART_RX_Complete ;USART Receive Complete Interrupt Vector 
Address

RJMP mainloop

Initialize:

    ;Stack
    LDI   temp, LOW(RAMEND)
    OUT   SPL, temp
    LDI   temp, HIGH(RAMEND)
    OUT   SPH, temp

USART_Init:
;    Set baud rate
    LDI  temp, HIGH(UBRR_VAL)
    OUT  UBRRH, temp
;    LDI  temp, 0x0F ;Asynchron, 14.7456 MHz, 115200Baud
    LDI  temp, LOW(UBRR_VAL)
    OUT  UBRRL, temp

    LDI  temp, 0x00
;    LDI  temp, (1<<U2X) ;Set to asynchonius mode
    OUT  UCSRA, temp

    ;Enable receiver and transmitter
    LDI     temp, 0x00
;    LDI     temp, (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN)
    LDI  temp, (1<<RXCIE)|(1<<RXEN)|(1<<TXEN)
;    OUT  UCSRB, temp
    SBI  UCSRB,TXEN

    ;Protocol: 8,N,1
    LDI  temp, 0x00
    LDI  temp, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)
    OUT  UCSRC, temp


    LDI   temp, 0b11111111
    OUT   DDRC, temp ;Port C for Output

;    SEI ;Enable Interupts

RET

MainLoop:
    RCALL  Sync
    RCALL  SerOut
    OUT   PORTC, count
    INC    count
RJMP MainLoop

USART_RX_Complete:
    PUSH  temp ;Save temporary Variable
    IN  temp, SREG
    PUSH  temp ;Save Status-Register

    IN  received, UDR ;Byte from USART
    OUT   PORTC, received

    POP   temp ;Restore Status-Register
    OUT   SREG, temp
    POP   temp ;Retore temporary Variable

RETI

SerOut:
    LDI     Temp, 31
    SBIS    UCSRA,UDRE
    RJMP    SerOut
    OUT     UDR, temp

RET

Sync:
    LDI     counter1,0
Sync_1:
    LDI     counter2,0
Sync_2:
    LDI     counter3,0
;Sync_3:
;    DEC     counter3
;    BRNE    Sync_3
    DEC     counter2
    BRNE    Sync_2
    DEC     counter1
    BRNE    Sync_1

RET
///
Wie Du siehst, ist da wirklich nichts hinter, Die Empfangsroutine habe 
ich seit Gestern nicht geändert. Ich habe nur den Interrupt disabled und 
die Senderoutine eingebaut.

--
Gruß Karsten

von Weingut P. (weinbauer)


Lesenswert?

Hardwareproblem? Pegelwandler? Mit dem Oszi schonmal die Flanken 
angeschaut?

von Karsten S. (scottyrebel)


Lesenswert?

Fhutdhb Ufzjjuz schrieb:
> Hardwareproblem? Pegelwandler? Mit dem Oszi schonmal die Flanken
> angeschaut?

Hallo,
der FT232RL funktioniert einwandfrei(den habe ich in einer anderen 
Anwendung getestet), auch der µC scheint nicht defekt zu sein(Schon 
ausgetauscht).
--
Gruß Karsten

von Hc Z. (mizch)


Lesenswert?

Welches Zeichen erscheint denn?  Was mich stutzig macht:  Du machst
1
 ldi     temp, 31
2
 [...]
3
 OUT     UDR, temp
Damit sendest Du aber keine '1' (0x31), sondern das Kontrollzeichen 
0x1f.  Keine Ahnung, wie Dein Terminalprogramm das darstellt.

Bei abweichender Baudrate oder sonstigen Fehlern auf der 
Übertragungsstrecke kann aber durchaus ein Zeichen ankommen (oder auch 
mehrere, wenn die Sendebaudrate deutlich unter der Empfangsbaudrate 
liegt).  Wichtig ist also in diesem Fall, dass das richtige Zeichen 
ankommt.  Kommt denn wirklich 0x1f an?

von Michael B. (mb_)


Lesenswert?

Karsten Sosna schrieb:
> Interrupt sondern Polling gehe. Hänge jetzt mal bestehenden Code an.

> .org 0x00
>     RJMP Initialize ;Reset

> Initialize:
>
>     ;Stack
>     LDI   temp, LOW(RAMEND)
>     OUT   SPL, temp
>     LDI   temp, HIGH(RAMEND)
>     OUT   SPH, temp
>
> USART_Init:
> ;    Set baud rate
....
> RET

Ich frage mich wie das da überhaupt funktionieren kann.
Vom Reset vector springst du in Initialize, danach fällst du in 
USART_Init durch und dann machst du ein RET. Wohin soll es denn 
RETurnen?

von Karsten S. (scottyrebel)


Lesenswert?

> Welches Zeichen erscheint denn?  Was mich stutzig macht:  Du machst
>
1
>  ldi     temp, 31
2
>  [...]
3
>  OUT     UDR, temp
4
>
> Damit sendest Du aber keine '1' (0x31), sondern das Kontrollzeichen
> 0x1f.  Keine Ahnung, wie Dein Terminalprogramm das darstellt.

Hallo,
das ist mir schon klar und war auch so gewollt, übertragen werden 
grunsätzlich binäre Daten. Daher ist es egal, wenn ich 31 sende muss auf 
der PC-Seite 31(oder 0x1F) abkommen und das tat es.
Nichts desto trotz lief der µC auch dann im Empfang mit Interrupt. 2-3 
mal und dann war wieder Schluß, nur noch Schrott. Also dachte ich es 
liegt vielleicht an der CKOPT-Fuse, also wieder umgesetzt, danach ging 
gar nichts mehr, also wieder zurückgesetzt. Gleiches Ergebnis. :=(
Also habe ich etwas Forschung betrieben und festgestellt, das der µC 
zeitweilig resetet wurde, ergo habe ich ihn mal ausgetauscht, gleiches 
Ergebnis. Also konnte es nur am Programm liegen. Dann kam die 
Erleuchtung, bei dem Reset handelt es sich ja nicht um ein 
Hardware-Reset sondern um ein Software-Reset und damit standen meine 
Register im Nirwana. Jetzt habe ich die Register sauber initialisiert 
und siehe da alles funktioniert bestens. Ich habe jetzt meine 
Übertragung auf 230400, 8, N, 1, Asynchron und keine Probleme.
Also hier sind zwei Dinge zusammen gefallen. Das eine war die falsch 
eingestellte Fuse und das andere die falsch initialisierten Register 
womit ich dann eine falsche Anzeige hatte.

So nun läuft er wie ich mir das vorstelle. Ich bedanke mich daher ganz 
besonders bei Dir und natürlich auch bei allen anderen für die 
Unterstützung.
--
Gruß Karsten

von Karsten S. (scottyrebel)


Lesenswert?

> Ich frage mich wie das da überhaupt funktionieren kann.
> Vom Reset vector springst du in Initialize, danach fällst du in
> USART_Init durch und dann machst du ein RET. Wohin soll es denn
> RETurnen?

Hallo Michael,
da ist was beim Formartieren schief gegangen. Wenn ch den Code aus dem 
AVR-Studio hierher kopiere kann das kein Mensch mehr lesen.
Das RET steht da nicht im Orignal, weiß nicht wie es da hin gekommen 
ist.
--
Gruß Karsten

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.