Forum: Mikrocontroller und Digitale Elektronik Problem mit RX/TX und Timer0 - zusammen geht es nicht


von Markus Reichert (Gast)


Lesenswert?

Hallo zusammen,

ich habe mich zum ersten Mal an den Timer0 gewagt und schon
funktioniert es nicht mehr.

Ich habe ein einfach Testprogramm geschrieben, welches auf dem STK500
abläuft. Tasten 0-3 werden auf den entsprechenden LEDs ausgegeben. Im
Hintergrund läuft der Timer0 mit Overflow-Interrupt. Bei Interrupt
werden die Tasten 4-7 abgefragt und auf den LEDs ausgegeben. Ausserdem
sollen Zeichen über die RS232 Schnittstelle gesendet werden. Aber er
bleibt irgendwo hängen. Wenn ich die folgenden 2 Zeilen
ausdokumentiere, sendet er zwar etwas, aber es funktioniert nicht
richtig:

sbis UCSRA,UDRE          ;HIER LIEGT DAS PROBLEM
rjmp USART_Transmit

Ich habe das komplette Programm unten eingefügt.

Die Routine zum Senden funktioniert alleine wunderbar. Ich habe sie
schon mehrfach verwendet. Aber irgendetwas scheint den Befehl oben zu
beeinflussen - aber ich habe keine Ahnung was.

Ich hoffe, jemand kann mir den Fehler sagen, den ich da eingebaut habe.
:-)

Im Voraus vielen Dank.

Gruß
Markus



    .INCLUDE "m16def.inc"

    .DEF  akku =  r16;
    .DEF  temp =  r17;
    .DEF  seku =  r18;
    .DEF  send =  r19;
    .CSEG
    rjmp  start
    .ORG  $012
    rjmp  timer0
    .ORG  $030

start:  ldi    akku,LOW(RAMEND);
    out    SPL,akku;

      ldi   temp, HIGH(RAMEND)      ; HIGH-Byte der obersten RAM-Adresse
        out   SPH, temp

    ldi    akku,$ff        ;PortB als Ausgang
    out    DDRB,akku

    ldi    akku, 0b010        ;Timer0 Einstellungen
    out    TCCR0, akku

    in    akku, TIMSK
    ori    akku, 0b00000001
    out    TIMSK, akku

    rcall  USART_Init        ;Initialisierung USART
    sei

abfrage:
    in    akku, PINA        ;SW0 - SW3 auf LED0 - LED3 ausgeben
    andi  akku, 0b00001111
    ori    akku, 0b11110000
    out    PORTB, akku
    rjmp  abfrage

timer0:
    cli                ;Interrupts ausschalten

taste:  in    akku, PINA        ;SW4 - SW7 auf LED4 - LED7 ausgeben
    andi  akku, 0b11110000
    ori    akku, 0b00001111
    out    PORTB, akku
    rcall  zeichen          ;Zeichen senden
    cpi    akku, 0b11111111
    brne  taste          ;schleife, wenn taste weiterhin gedrückt
    sei                ;Interrupts einschalten
    reti


USART_Init:
;set baud rate

ldi r17,0
ldi r16, 23

out UBRRH, r17
out UBRRL, r16

;Enable receiver and transmitter

ldi r16, (0<<RXCIE) | (0<<RXEN) | (1<<TXEN)
out UCSRB,r16

;set frame format: 8data, 1stop bit

ldi r16, (1<<URSEL) | (0<<USBS) | (3<<UCSZ0)
out UCSRC,r16

ret


USART_Receive:

sbis UCSRA, RXC
rjmp USART_Receive
in send, UDR
out    PORTB, temp
ret



USART_Transmit:

sbis UCSRA,UDRE          ;HIER LIEGT DAS PROBLEM, bei ausdokumentieren
funktioniert es, aber nicht richtig
rjmp USART_Transmit
out UDR,temp
ret



zeichen:

  ldi temp, 0x81
  rcall USART_Transmit
  ldi temp, 0x82
  rcall USART_Transmit

  ret

von A.K. (Gast)


Lesenswert?

Ein AVR hat nur einen Registersatz, daher dürfen durch Interrupts keine
Register verändert werden, wenn das nicht ausdrückliche Absicht ist.
Hier werden beispielsweise "akku" und "temp" durch einen Interrupt
vernichtet.

Und in SEI/RETI ist SEI zwar nicht schädlich aber überflüssig, das
macht der RETI gleich mit.

von Markus Reichert (Gast)


Lesenswert?

Ersteinmal vielen Dank für die superschnelle Antwort. .-)

In meinem Fall ist es doch egal, ob "akku" und "temp" vernichtet
werden. Oder?

Ich lade doch temp und rufe danach gleich "USART_Transmit" auf. Davor
und danach ist es mir doch egal, was in den beiden Registern steht.

Oder habe ich da einen Denkfehler?

von H.joachim S. (crazy_horse)


Lesenswert?

-cli und sei brauchst du in der ISR nicht
-zumindest das SREG muss in der ISR gesichert werden

von A.K. (Gast)


Lesenswert?

Was passiert, wenn zwischen
    in    akku, PINA        ;SW0 - SW3 auf LED0 - LED3 ausgeben
und
    andi  akku, 0b00001111
"akku" per Interrupt einen anderen Wert kriegt?

Bei deinem Code zwar garnichts, weil im Interrupt exakt das gleiche
steht wie im Hauptprogramm (was übrigens auch nicht allzu viel Sinn
ergibt), aber es ist saumässig schlechter Stil und man stolpert später
garantiert drüber.

Genauso sollte man in Interrupts generell keine längeren Pausen
einlegen. Also sollte man in einem Timer-Interrupt nichts auf UART
ausgeben, weil da eine Warteschleife drin steckt.

von Wolfram (Gast)


Lesenswert?

Wenn ich mir dein Programm anschaue würde ich sagen du hast 3,6 MHz und
sendest mit 9600 Baud. d.h. ein Zeichen zu senden dauert 1ms.
Aus einer Timerroutine Aktionen auszulösen die 1ms und länger dauern
würde ich als Denkfehler bezeichnen. Setze im Timerinterrupt ein Flag
und polle dieses im Hauptprogramm.

Timerinterrupt
{setze Flag}

Hauptprogramm:
1. wenn Flag gesetzt senden/empfangen/Tasten abfragen
2. zu 1.

von Markus Reichert (Gast)


Lesenswert?

@A.K.

ich weiß, dass das Programm nicht besonders sinnvoll ist. Soll ja auch
nur ein Testprogramm sein. Ich möchte das weiterentwickeln, damit es
mir mal eine 5x4-Matrixtastatur auswertet.
Der Kniff (in meinen Augen ist dabei), dass wenn die Taste gedrückt
wird ein Befehl gesendet werden soll und wenn die Taste losgelassen
wird soll wieder ein anderer Befehl gesendet werden (GO-STOP).
Deswegen habe ich versucht, das ganze über einen Timer zu lösen.


@Wolfram + A.K.

danke für die (Lösungs-)Hinweise. Da ich bisher noch nichts mit Flags
gemacht habe, werde ich mich mal damit auseinandersetzen.


@Wolfram

deine Vermutungen stimmen: 3,6 Mhz und 9600 Baud

von Wolfram (Gast)


Lesenswert?

Nur zur Klarstellung:
Deine Programmstruktur geht prinzipiell du schaltest in der
Timerroutine
ja die Interrupts aus, sobald du mit dieser Struktur aber größere
Programme schreiben willst wirst du grosse Probleme bekommen, also
gewöhn dir das erst gar nicht an.
>;Enable receiver and transmitter
>ldi r16, (0<<RXCIE) | (0<<RXEN) | (1<<TXEN)
damit schaltest du NUR den Tranceiver ein
>ldi r16, (0<<RXEN) | (1<<TXEN)
schaltet Receiver und Transceiver ein

BTW: Überprüf mal in den FUSES das wirklich der externe Takt benutzt
wird

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.