Unerwartete Atmega16-Reaktion über UART

8

Unerwartete Atmega16-Reaktion über UART

Kurze Zusammenfassung des Problems

Ich habe einen Atmega16 mit Code geflasht, der dazu führen sollte, dass der Atmega16 jedes Zeichen zurücksendet, das ich über ein Terminal an ihn sende. Ich bekomme eine Antwort, aber es ist selten der Charakter, den ich gesendet habe. Ich kann die korrekte Ausgabe sehen, indem ich die Baudrate ändere, aber ich verstehe nicht, warum die richtige Baudrate funktioniert.

Mehr Details

Ich versuche in meiner Freizeit mehr über Firmware-Programmierung zu lernen, weil es mir sehr gut gefällt. Bisher haben wir in der Firmware-Programmierung, die ich an der Uni durchgeführt habe, Skeleton-Code-Dateien erhalten, die einen Großteil der Peripherie-Schnittstellen übernehmen und für uns eingerichtet sind, aber ich würde dies gerne selbst lernen. Ich habe ein paar Fragen darüber, was ich hier mache, die im ganzen Beitrag verteilt sind, aber ich werde sie am Ende auflisten. Wenn Sie Missverständnisse oder potenzielle Wissenslücken aufgreifen, würde ich mich über Ihre Beiträge sehr freuen.

Der Code

Der Code, den ich auf meinem Atmega16 geflasht habe, stammt fast Zeile für Zeile aus dem Tutorial "Verwenden des USART in AVR-GCC" auf dieser Seite . Alles was ich hinzugefügt habe ist das #define für F_CPU. Der ursprüngliche Code hatte keine #define für F_CPU, sodass mein Code in AtmelStudio 7 nicht kompiliert werden konnte. Kann jemand erklären, warum der Autor F_CPU nicht in seiner Originaldatei definiert hätte? Ich vermute, sie haben möglicherweise ein anderes Tool oder einen anderen Compiler als Atmel Studio 7 verwendet, aber ich kann es nicht mit Sicherheit sagen.

#include <avr/io.h>
#define F_CPU 7372800 //this was chosen because the tutorial states this is the frequency we want to operate at
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((( F_CPU / 16) + ( USART_BAUDRATE / 2)) / ( USART_BAUDRATE )) - 1)

int main ( void )
{
    char ReceivedByte ;
    UCSRB = (1 << RXEN ) | (1 << TXEN ); // Turn on the transmission and reception circuitry
    UCSRC = (1 << URSEL ) | (1 << UCSZ0 ) | (1 << UCSZ1 ); // Use 8- bit character sizes
    UBRRH = ( BAUD_PRESCALE >> 8); // Load upper 8- bits of the baud rate value into the high byte of the UBRR register
    UBRRL = BAUD_PRESCALE ; // Load lower 8- bits of the baud rate value into the low byte of theUBRR register
    for (;;) // Loop forever
    {
        while (( UCSRA & (1 << RXC )) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
        ReceivedByte = UDR ; // Fetch the received byte value into the variable " ByteReceived "
        while (( UCSRA & (1 << UDRE )) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
        UDR = ReceivedByte ; // Echo back the received byte back to the computer
    }
}

Das Hardware-Setup

Foto des Hardware-Setups

  • MCU: Atmega16;
  • Toolchain: Atmel Studio 7, blinkt mit AVR-Drache;
  • Stromversorgung: 5-V-Schiene von einem von einer Universität bereitgestellten Entwicklungsboard (das vom Computer-USB stammt). 100nF Keramikscheibenkondensator zur Umgehung der Steckbrettstromleitungen
  • USB zu seriellem Konverter: Dieser . TXD am USB-Seriell-Wandler an RXD Atmega angeschlossen (Pin 15). RXD am Konverter an RXD an Atmega angeschlossen (Pin 14).
  • Terminalsoftware: PuTTY (mit einer Baudrate von 9600).

    Nachweis der falschen Antworten

    Um es zu wiederholen, die Atmega sollte zurückkehren , was es heißt OUTPUT gesendet wurde sollte genau die gleichen wie INPUT sein.

    PuTTY-Ausgabe

    EINGANGAUSGABEf&f6z>d0Platz0x8

    Oszilloskop-Aufnahmen

    Ich habe mein Picoscope mit serieller Decodierung verwendet, um zu überprüfen, ob der Atmega die richtige Eingabe empfängt, die es zu sein scheint. Wenn ich zum Beispiel die Taste 'f' drücke, wird sie korrekt empfangen. Die Ausgabe ist immer noch eine '6' (oder gelegentlich ein kaufmännisches Und '&').

Scope Capture am RX-Pin des Atmega16 zeigt an, dass das richtige Zeichen über die Terminalsoftware gesendet wird ('f').

Scope Capture am TX-Pin des Atmega16 zeigt an, dass eine unerwünschte Antwort zurückgesendet wird ('6')

Ein Fix, auf den ich gestoßen bin, den ich nicht verstehe

Wenn ich die Baudrate auf 2500 in PuTTY ändere, wird alles korrekt angezeigt. Ich habe diesen Wert zufällig ausgewählt und weiß nicht, warum er funktioniert (es lässt mich glauben, dass ich irgendwo einen Fehler mit der Baudrate gemacht habe, aber ich sehe nicht, wo ich das Tutorial fast genau kopiert habe ... ich habe gedacht).

Fragen

  1. Was habe ich falsch gemacht / was passiert hier?
  2. Warum definiert das ursprüngliche Tutorial F_CPU nicht #?
  3. Warum behebt das Festlegen der Baudrate auf 2500 das Problem? (Ich vermute, dass dies beantwortet wird, wenn Frage 1 beantwortet wird)
Daviegravee
quelle
2
Durch einfaches Definieren von F_CPU auf einen bestimmten Wert wird das Mikro nicht mit dieser Frequenz ausgeführt. F_CPU sollte als die Häufigkeit definiert werden, mit der Sie das Mikro für die Ausführung konfiguriert haben - aber ich sehe keine Beweise dafür, dass Sie dies irgendwo konfiguriert haben ...
brhans
Gut geschriebene Frage. Das einzige, was es verbessern würde, wäre ein Schaltplan.
Blair Fonville
L.EINT.E.X.
Ich stelle fest, dass Sie keinen externen Kristall auf Ihrem Steckbrett haben. Verwenden Sie die interne RC-Uhr? Mit welcher Frequenz läuft der Prozessor voraussichtlich?
Scotty3785
Dank Ihrer Diskussion über F_CPU habe ich einige Nachforschungen angestellt und die Lösung veröffentlicht. Ich stelle mir vor, es ist für Sie offensichtlich (so wie es jetzt für mich ist ), aber es könnte jemand anderem helfen.
Daviegravee

Antworten:

0

Ich habe es herausgefunden! Dank der Kommentare zu F_CPU als Antwort auf das OP habe ich einige Nachforschungen angestellt (dies könnte für Sie alle offensichtlich sein).

Kurze Zusammenfassung der Lösung

Der Atmega16 lief nicht mit der Frequenz, die ich dachte, weil ich nicht verstand, wie man seine Systemfrequenz ändert. Beim Einchecken der Sicherungen in Atmel Studio konnte ich feststellen, dass ich mit 2 MHz lief (dies ist meines Wissens nicht die Standardtaktfrequenz, aber ich werde nicht darauf eingehen ) und nicht mit 7,3728 MHz wie im Tutorial.

F_CPU hat nicht die Frequenztakt Änderung der MCU (die Atmega16). Die Frequenz des Atmega16 wurde nicht auf 7,3728 MHz geändert, wie dies erforderlich war, damit das Codebeispiel funktioniert. Es lief immer noch mit der von den Sicherungen definierten Frequenz (in diesem Fall 2 MHz , mehr dazu weiter unten), sodass die Papierberechnung der gewünschten Baudrate von der tatsächlich verwendeten abweicht.

Arbeitscode

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 2000000 //THIS LINE IS **NOT** CHANGING THE FREQUENCY OF THE MCU: CHANGE MCU FREQUENCY IN FUSES
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((( F_CPU / 16) + ( USART_BAUDRATE / 2)) / ( USART_BAUDRATE )) - 1)

int main ( void ){
    char ReceivedByte ;
    UCSRB = (1 << RXEN ) | (1 << TXEN ); // Turn on the transmission and reception circuitry
    UCSRC = (1 << URSEL ) | (1 << UCSZ0 ) | (1 << UCSZ1 ); // Use 8- bit character sizes
    UBRRH = ( BAUD_PRESCALE >> 8); // Load upper 8- bits of the baud rate value into the high byte of the UBRR register
    UBRRL = BAUD_PRESCALE ; // Load lower 8- bits of the baud rate value into the low byte of theUBRR register
    for (;;){ // Loop forever
        while (( UCSRA & (1 << RXC )) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
        ReceivedByte = UDR ; // Fetch the received byte value into the variable " ByteReceived "
        while (( UCSRA & (1 << UDRE )) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
        UDR = ReceivedByte ; // Echo back the received byte back to the computer
    }
}

Mehr Details

Gewünschte Baudrate gegen das, was die Atmega tatsächlich tat

Die gewünschte Baudrate (aus dem Tutorial) betrug 9600, die Baudrate, die ich in PuTTY verwendet habe. Die tatsächliche Baudrate kann anhand der hervorgehobenen Gleichung in Tabelle 60 (Seite 147) des Atmega16-Datenblattes berechnet werden.

Gleichungstabelle zur Berechnung von Baudrate und UBRR ab Seite 147 Atmega16-Datenblatt

Im Codebeispiel BAUD_PRESCALEist UBRR in der Berechnung. BAUD_PRESCALEwird mit den für F_CPUund definierten Werten als 47 bewertet USART_BAUDRATE.

BAUD=fÖsc16(UBRR+1)
BAUD=2,000,00016(47+1)
BAUD2,604

Und das war die Wurzel des Problems. Der Atmega16 arbeitete mit 2 MHz, was bedeutete, dass sich der Wert von f_ {osc} vom Beispiel des Tutorials unterschied, was zu einer Baudrate von 2.604 gegenüber 9.600 führte.

Beachten Sie, dass f_osc die tatsächliche Systemfrequenz der MCU ist, die nicht durch bestimmt wird F_CPU.

Damit ist auch meine dritte Frage beantwortet: Die Änderung der Baudrate auf 2.500 lag glücklicherweise nahe genug an der Betriebsbaudrate der MCU, sodass das Terminal die Ergebnisse korrekt interpretieren konnte.

Ändern der Frequenz der MCU

So ändern Sie die Frequenz der MCU in AtmelStudio 7:

Tools > Device programming > Fuses > Change SUT_CKSEL (or LOW.SUT_CKSEL in my case) to desired frequency (make sure you have read up on the side effects of this). 

Die im Beispiel verwendete Frequenz ist keine interne Standardtaktfrequenz, daher bleibe ich bei 2 MHz.

Zusammenfassung der Antworten auf meine eigenen Fragen

  1. Was habe ich falsch gemacht / was passiert hier? Antwort : Die Taktfrequenz wurde im Lernprogramm nicht auf die Taktfrequenz geändert, was zu einer anderen Baudrate führte als erwartet, wodurch die Terminalsoftware (PuTTY) nicht mehr mit der MCU synchronisiert wurde
  2. Warum definiert das ursprüngliche Tutorial F_CPU nicht #? Antwort : Immer noch nicht ganz sicher, aber ich vermute, dass es in einem Makefile definiert ist, das nicht im Tutorial angegeben ist, und dass der Autor keine IDE wie Atmel Studio verwendet hat
  3. Warum behebt das Festlegen der Baudrate auf 2500 das Problem? (Ich vermute, dass dies beantwortet wird, wenn Frage 1 beantwortet wird.) Antwort : Glücklicherweise wurde eine Zahl nahe der Baudrate des Atmega16 erraten
Daviegravee
quelle