USART ISR wiederholt sich 4 Mal

Hallo,

Ich habe ein Problem mit dem USART_RX_vect-Interrupt des ATMega 328p. Das Empfangen funktioniert einwandfrei und ich kann die gesendeten Bits auswerten (es liegt also nicht an der Baudrate), aber jedes Mal, wenn ich ein Bit sende, wird die ISR 4 Mal ausgeführt.​

Also eine ISR wie diese:

ISR(USART_RX_vect) {
    uart_transmit('t');
    uart_flush();
}

Wird {'t', 't', 't', 't'}übertragen

Ich habe keine Ahnung, warum das passiert, und ich hoffe, Sie können mir helfen.


My mainist eine leere Schleife (natürlich führe ich aus uart_setup). Mein ISR ist wie oben. Ich verwende avr-gcc unter Linux.

Dies ist mein UART-Setup:

void uart_setup() {                                                                                                                                                                                                 
      UBRR0H = (BAUDRATE >> 8);                                                                                                                                                                                       
      UBRR0L = BAUDRATE;                                                                                                                                                                                              

      // Set 8-Bit mode                                                                                                                                                                                               
      setBit(UCSR0C, UCSZ01);                                                                                                                                                                                         
       setBit(UCSR0C, UCSZ00);                                                                                                                                                                                         

      setBit(UCSR0B, RXEN0); // Receiver enable                                                                                                                                                                       
      setBit(UCSR0B, TXEN0); // Transmitter enable                                                                                                                                                                    

      setBit(UCSR0B, RXCIE0); // Enable RX Interrupt                                                                                                                                                                  
      sei(); // Enable global interrupts                                                                                                                                                                              
  }       

void uart_transmit(unsigned char data)                                                                 
{                                                                                                      
    while (!( UCSR0A & (1<<UDRE0))); // wait while register is free                     
    UDR0 = data; // load data in the register                        
}         

void uart_flush( void ) {                                                                              
          unsigned char dummy;                                                                           
          while ( UCSR0A & (1<<RXC0) ) dummy = UDR0;                                                     
  }                                                                                                                                                                                                                                                                                                                                                   

Antworten (2)

Es scheint, dass das Design Ihres Codes so ist, dass Sie, wenn ein Byte im UART empfangen wird, dieses Byte entsorgen und mit der Übertragung eines "t" antworten möchten.

Der UART nutzt ein gemeinsames Empfangs-/Sendepufferregister. Da Ihre UART-Empfangsroutine interruptgesteuert ist, sollten Sie das empfangene Zeichen sofort lesen, bevor Sie die Antwort in den Puffer legen, da Sie sonst den Empfangsinterrupt beim Beenden erneut auslösen.

Es gibt auch keinen Grund, den 'while'-Code in der uart_flush-Routine zu haben, da Sie wissen, dass sich ein Zeichen im Puffer befindet, basierend auf der Tatsache, dass ein Interrupt Sie an diesen Punkt gebracht hat. Lesen Sie einfach das Pufferregister.

Hier ist eine vereinfachte Version eines korrigierten ISR ohne die Funktionen:

ISR(USART_RX_vect) {
     unsigned char dummy;
     dummy = UDR0;                     // dump the rcvr buffer
     while (!( UCSR0A & (1<<UDRE0)));  // wait until the register is free                     
     UDR0 = data;                      // load xmt data in the register
}

Die Funktion uart_flush()dient meines Erachtens keinem Zweck, es sei denn, Sie verwenden sie, um auf den Empfang aller Bytes zu warten? Ich denke, Ihr Problem ist, dass Sie die Dinge in umgekehrter Reihenfolge tun, Sie erhalten den Rx-Interrupt und schreiben dann in das UART-Datenregister und lesen dann daraus, ohne Garantie dafür, dass die Übertragung abgeschlossen ist. Ich denke, Sie müssen die Reihenfolge, in der Sie die Übertragung anrufen, tauschen und in Ihrer ISR spülen.