Forum: Mikrocontroller und Digitale Elektronik AVR (ATMega8) Interrupt UART TXCIE will nicht


von pacer (Gast)


Lesenswert?

Hallo,

Ich versuche Zeichen an das Hyperterminal zu senden. Dazu soll der 
Interrupt für "Zeichen gesendet" ausgenutzt werden. Leider wird der 
Interrupt nicht ausgelöst.

Mein Programm sieht wie folgt aus:
Ich suche den Feler jetzt schon seit 3 Tagen und ich komme einfach nicht 
drauf :-(


 .INCLUDE <m8def.inc>
 .def temp = R16        ; für Interrupt
 .def ZEICHEN = R17

 ;------------------------------------------------------
 ;    Peripherie initialisieren
 ;------------------------------------------------------
.equ F_CPU = 7273800                            ; Systemtakt in Hz
.equ BAUD  = 9600                               ; Baudrate

; Berechnungen
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate


 ;------------------------------------------------------
 ;     Start Adresse 0000 /Interruptvektoren
 ;------------------------------------------------------
 .org 0x000
  rjmp Init      ; Interruptvektoren überspringen

 .org UTXCaddr      ; UART Transmit Complete Interrupt
    rjmp serout


 ;------------------------------------------------------
 ;     INITIALIZE
 ;------------------------------------------------------
INIT:
;Stack Pointer setzen
  ldi temp,high(RAMEND)
  out SPH,temp
  ldi temp,low(RAMEND)
  out SPL,temp

; Baudrate einstellen
  ldi temp, HIGH(UBRR_VAL)
  out UBRRH, temp
  ldi temp, LOW(UBRR_VAL)
  out UBRRL, temp

; Frame-Format: 1 Stop-Bits, 8 Bit
  ldi temp, (1<<URSEL)|(0<<USBS)|(1<<UCSZ1)|(1<<UCSZ0)
  out UCSRC, temp
  sbi UCSRB,TXEN                 ; Sender aktivieren
  sbi UCSRB,TXCIE    ; Interrupt für "senden fertig" aktivieren

  sei        ; globale Interruptfreigabe

  sbi UCSRA,TXC        ; Initialisierung des Interrupts
 ;------------------------------------------------------
 ;   HAUPTSCHLEIFE
 ;------------------------------------------------------
Hauptschleife:


rjmp Hauptschleife

 ;------------------------------------------------------
 ;   Subroutinen / ISRs
 ;------------------------------------------------------

serout:
   lpm      ; Erstes Byte des Strings nach R0 lesen

   mov ZEICHEN,R0
   adiw ZL, 1             ; Adresse des Z-Pointers um 1 erhöhen
   tst R0    ; Inhalt von R0 auf Bull testen

   brbc 1,sprung_b
    ldi ZL, LOW(Daten*2)  ; Adresse des ersten Strings in den
    ldi ZH, HIGH(Daten*2)  ; Z-Pointer laden
   sprung_b:

    out     UDR, ZEICHEN  ; Zeichen auf UART ausgeben
reti

 ;------------------------------------------------------
 ;   Datenbereich
 ;------------------------------------------------------
Daten:
           .db 0x12, 0x12
       .db 0x20, 0x20
       .db 0x63, 0x63
       .db 0x58, 0x00

von Falk B. (falk)


Lesenswert?

@ pacer (Gast)

>Ich versuche Zeichen an das Hyperterminal zu senden. Dazu soll der
>Interrupt für "Zeichen gesendet" ausgenutzt werden. Leider wird der
>Interrupt nicht ausgelöst.

>Mein Programm sieht wie folgt aus:
>Ich suche den Feler jetzt schon seit 3 Tagen und ich komme einfach nicht
>drauf :-(

Ich glaube du hast einige wesentliche Dinge noch nicht verstanden.

AVR-Tutorial: UART
Interrupt

>  sei        ; globale Interruptfreigabe

Bis hier hin alles OK.

>  sbi UCSRA,TXC        ; Initialisierung des Interrupts

Was soll das? Dieser Befehl löscht ein anstehendes Interrupt-Bit.

> ;------------------------------------------------------
> ;   HAUPTSCHLEIFE
> ;------------------------------------------------------
>Hauptschleife:
>rjmp Hauptschleife

Kannst du mir sagen, wo hier Zeichen gesendet werden?

>serout:

Du verwechselst die Funktionalität von UDRE und TXC Interrupt!

MFG
Falk

von pacer (Gast)


Lesenswert?

Danke für deine Hilfe

Das Tutorial habe ich mir schon angeschaut, leider wird in den 
Beispielen der Interrupt nicht verwendet.
Stimmt, das setzten des Bits löscht das Interruptflag. Hab ich mich wohl 
verlesen.

Die Daten werden in der Interruptroutine ausgegeben, deswegen ist das 
Hauptprogramm leer.

Nur muss ich zumindest einmal den Interrupt auslösen. Vielleicht könnte 
ich ja ein Leerzeichen senden wenn es losgehen soll, oder irgendetwas 
anderes?!

von Falk B. (falk)


Lesenswert?

@ pacer (Gast)

>Nur muss ich zumindest einmal den Interrupt auslösen. Vielleicht könnte
>ich ja ein Leerzeichen senden wenn es losgehen soll,

Das geht.

MFG
Falk

von Marvin M. (Gast)


Lesenswert?

.equ F_CPU = 7273800                            ; Systemtakt in Hz

Da ist aber ein Zahlendreher drin....

von pacer (Gast)


Lesenswert?

Stimmt, so ganz klar ist mir der Unterschied zwischen UDRE und TXC nicht 
klar.
Ich bin einfach davon ausgegangen, dass UDRE bedeutet, dass das Zeichen 
übermittelt wurde, unabhängig vom Status des Schieberegisters

TXE bedeutet, dass das Zeichen übermittelt wurde und das Schieberegister 
kein neues Zeichen enthält.

von pacer (Gast)


Lesenswert?

Ups, stimmt, die Frequenz ist tatsächlisch falsch, hat aber bis jetzt 
trotzdem funktioniert ;-)

ich werde das aber trotzdem verbessern, danke für den Tip

von pacer (Gast)


Lesenswert?

habe mein Programm wie folgt geändert:
Die Null für die Initialisierung kommt an, nur ein Interrupt wird immer 
noch nicht ausgelöst.
Ich hab keinen Plan was ich noch tun soll...

-------------------------------------------------------------------
 .include "C:\PROGRA~1\VMLAB\include\m8def.inc

 .def temp = R16        ; für Interrupt
 .def ZEICHEN = R17

 ;------------------------------------------------------
 ;    Peripherie initialisieren
 ;------------------------------------------------------
.equ F_CPU = 7372800                            ; Systemtakt in Hz
.equ BAUD  = 9600                               ; Baudrate

; Berechnungen
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate

 ;------------------------------------------------------
 ;     Start Adresse 0000 /Interruptvektoren
 ;------------------------------------------------------
 .org 0x000
  rjmp Init      ; Interruptvektoren überspringen

 .org UTXCaddr      ; UART Transmit Complete Interrupt
   rjmp serout

 ;------------------------------------------------------
 ;     INITIALIZE
 ;------------------------------------------------------
INIT:
;Stack Pointer setzen
  ldi temp,high(RAMEND)
  out SPH,temp
  ldi temp,low(RAMEND)
  out SPL,temp

; Baudrate einstellen
  ldi temp, HIGH(UBRR_VAL)
  out UBRRH, temp
  ldi temp, LOW(UBRR_VAL)
  out UBRRL, temp

; Frame-Format: 1 Stop-Bits, 8 Bit
  ldi temp, (1<<URSEL)|(0<<USBS)|(1<<UCSZ1)|(1<<UCSZ0)
  out UCSRC, temp
  sbi UCSRB,TXEN           ; Sender aktivieren
  sbi UCSRB,TXCIE  ; Interrupt für "senden fertig" aktivieren
  sei      ; globale Interruptfreigabe

  ldi ZEICHEN, '0'
  out UDR, ZEICHEN ;Initialisierung
 ;------------------------------------------------------
 ;   HAUPTSCHLEIFE
 ;------------------------------------------------------
Hauptschleife:

rjmp Hauptschleife

 ;------------------------------------------------------
 ;   Subroutinen / ISRs
 ;------------------------------------------------------

serout:
   lpm

   mov ZEICHEN,R0
   adiw ZL, 1
   tst R0

   brbc 1,sprung_b
    ldi ZL, LOW(Daten*2)
    ldi ZH, HIGH(Daten*2)
   sprung_b:

    out     UDR, ZEICHEN
reti

 ;------------------------------------------------------
 ;   Datenbereich
 ;------------------------------------------------------
Daten:
           .db 0x12, 0x12
       .db 0x20, 0x20
       .db 0x63, 0x63
       .db 0x58, 0x00

von Falk B. (falk)


Lesenswert?

@ pacer (Gast)

>habe mein Programm wie folgt geändert:
>Die Null für die Initialisierung kommt an, nur ein Interrupt wird immer
>noch nicht ausgelöst.
>Ich hab keinen Plan was ich noch tun soll...

U.a. SIMULIEREN!

>  ldi ZEICHEN, '0'
>  out UDR, ZEICHEN ;Initialisierung

Hier fehlt eine Initialisierung des Z-Pointers. Der kann sonstwohin 
zeigen.


>   brbc 1,sprung_b

Diesen Befehl nutz man praktisch NIE! Versuch mal lieber

    brne sprung_b

Sollte zwar das gleiche sein, aber naja.

MfG
Falk

von pacer (Gast)


Lesenswert?

okay, funktioniert doch. Eigentlich ist das Programm aus verschiedenen 
Tutorials zusammengestrickt. Der Teil mit dem Befehl bcrc ist zB aus dem 
AVR-Tutorial.

Den Z-Pointer brauche ich ja nur für den Datenbereich, bei einer 
Zuweisung muss ich ja nix tun. Müsste also korrekt sein. Simulieren tue 
ich mit VMLAB.

Wollte aber noch mall fragen ob ich das jetzt mit UDRE und TXC richtig 
verstanden habe?!

Gruß, pacer

von Falk B. (falk)


Lesenswert?

@ pacer (Gast)

>okay, funktioniert doch. E

Und woran lags?

>Wollte aber noch mall fragen ob ich das jetzt mit UDRE und TXC richtig
>verstanden habe?!

>>Ich bin einfach davon ausgegangen, dass UDRE bedeutet, dass das Zeichen
>>übermittelt wurde, unabhängig vom Status des Schieberegisters

Diese Formulierung ist nicht sehr glücklich. Übersetz doch einfach den 
Namen ins Deutsche

UDRE, UART Data Register Empty. Das Datenregister des UARTs ist leer. 
D.h. es kann neue Daten aufnehmen. Und dabei geht es um das 
Zwischenregister, mit dem die Senderichtigung gepufferet wird, praktisch 
ein kleiner FIFO.

>TXE bedeutet, dass das Zeichen übermittelt wurde und das Schieberegister
>kein neues Zeichen enthält.

Ja, diesen Interrupt nutzt man aber nur selten, z.B. bei 
Halbduplexkommunikation mit RS485, um den Tranceiver (Z.B. MAX485) von 
Senden auf Empfangen umzuschalten.

MFG
Falk

von Manfred (Gast)


Lesenswert?

Hallo,

ich schreibe zur Zeit ein Programm in Assembler für den ATMega162. Zur 
Simulation benutze ich das Programm VMLab.
Bei der Simulation mit der seriellen Schnittstelle habe ich Probleme.
Für den Interrupt nutze ich das Bit UDRIE1 im Register UCSR1B.
Die Daten liegen im Ausgabepuffer im Datenbereich und werden in der 
Interruptroutine über einen Zeiger gelesen und in das Register UDR1 
geschrieben.
Sobald die Daten in den Ausgabepuffer geschrieben wurden wird das Bit 
UDRIE1 gesetzt. Es wird sofort ein Interrupt ausgelöst und die Daten 
über die serielle Schnittstelle versendet. Am Ende der Interruptroutine 
wird das Bit UDRIE1 gelöscht.
Nachdem ein neuer Datenblock geschrieben wurde und das Bit UDRIE1 
gesetzt wurde, wird kein Interrupt ausgelöst.
Kann hier der Fehler bei VMLab liegen, da alle Flags richtig angezeigt 
werden?

Gruß

Manfred

von Falk B. (falk)


Lesenswert?

@  Manfred (Gast)

>Für den Interrupt nutze ich das Bit UDRIE1 im Register UCSR1B.
>Die Daten liegen im Ausgabepuffer im Datenbereich und werden in der

Was heisst "im Ausgangspuffer" ? Du meinst ein allgemeines Array.

>Interruptroutine über einen Zeiger gelesen und in das Register UDR1
>geschrieben.

>Sobald die Daten in den Ausgabepuffer geschrieben wurden wird das Bit
>UDRIE1 gesetzt.

>Es wird sofort ein Interrupt ausgelöst und die Daten
>über die serielle Schnittstelle versendet. Am Ende der Interruptroutine
>wird das Bit UDRIE1 gelöscht.

Wozu das? UDIE bleibt im Normalfall solange gesetzt, bis das letzte 
Zeichen in UDR1 geschrieben wurde.

>Nachdem ein neuer Datenblock geschrieben wurde und das Bit UDRIE1
>gesetzt wurde, wird kein Interrupt ausgelöst.
>Kann hier der Fehler bei VMLab liegen, da alle Flags richtig angezeigt
>werden?

Ja, kann ein Fehler sein.

MfG
Falk

von Manfred (Gast)


Lesenswert?

@Falk

Die Daten, die über die serielle Schnittstelle gesendet werden sollen 
befinden sich als Block im internen RAM. Das letzte Zeichen ist null.

In der Interruprroutine wird bei jedem Aufruf ein Zeichen gelesen und in 
das Register UDR1 geschrieben. Falls null gelsen wird, wird dieses 
Zeichen nicht in das Register UDR1 geschrieben, aber das Bit UDRIE1 
gelöscht.
Dieses Bit wird erst wieder gesetzt, nachdem ein neuer Block in das 
interne RAM geschrieben wurde.

Da die erste Übertragung funktioniert und dann nicht mehr, vermute ich 
einen Fehler bei VMLab.

Gruß

Manfred

von Falk B. (falk)


Lesenswert?

@ Manfred (Gast)

Genau so meinte ich das.

>Da die erste Übertragung funktioniert und dann nicht mehr, vermute ich
>einen Fehler bei VMLab.

Ja. Simulation ist nur die halbe Wahrheit. Probiers aus.

MfG
Falk

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.