Unerwartete Atmega16-Antwort über UART

Unerwartete Atmega16-Antwort ü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 das Zeichen, das 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 großen Spaß macht. Bisher haben wir in der Firmware-Programmierung, die ich an der Uni durchgeführt habe, Skelettcodedateien erhalten, die einen Großteil der Peripherieschnittstellen übernehmen und für uns einrichten, aber ich würde das gerne selbst lernen. Ich habe ein paar Fragen darüber, was ich hier tue, die über den gesamten Beitrag verstreut sind, aber ich werde sie am Ende auflisten. Wenn Sie Missverständnisse oder mögliche Wissenslücken aufgreifen, würde ich mich sehr über Ihren Beitrag freuen.

Der Code

Der Code, den ich auf meinen Atmega16 geflasht habe, stammt fast Zeile für Zeile aus dem Tutorial „Using the 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 kein #define für F_CPU, sodass mein Code nicht in AtmelStudio 7 kompiliert werden konnte. Könnte jemand erklären, warum der Autor F_CPU nicht in seiner ursprünglichen Datei definiert hätte? Ich vermute, dass sie möglicherweise ein anderes Tool oder einen anderen Compiler als Atmel Studio 7 verwendet haben, 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 Hardwareaufbaus

  • MCU: Atmega16;
  • Toolchain: Atmel Studio 7, flashen mit AVR Dragon;
  • Stromversorgung: 5-V-Schiene von einem von der Universität bereitgestellten Entwicklungsboard (das vom Computer-USB genommen wird). 100-nF-Keramikscheibenkondensator zur Umgehung der Stromversorgungsleitungen des Steckbretts
  • USB-zu-Seriell-Konverter: Dieser . TXD auf dem USB-Seriell-Konverter, der mit RXD Atmega (Pin 15) verbunden ist. RXD auf dem Konverter verbunden mit RXD auf Atmega (Pin 14).
  • Terminalsoftware: PuTTY (mit Baudrate 9600).

    Beweis für die falschen Antworten

    Um es noch einmal zu wiederholen, der Atmega sollte zurückgeben, was an ihn gesendet wurde, dh OUTPUT sollte genau das gleiche wie INPUT sein.

    PuTTY-Ausgabe

    EINGANG AUSGANG f & f 6 z > d 0 Platz 0 x 8

    Oszilloskop-Aufnahmen

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

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

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

Eine Lösung, über die ich gestolpert bin, die ich nicht verstehe

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

Fragen

  1. Was habe ich falsch gemacht/was ist hier los?
  2. Warum #define F_CPU im ursprünglichen Tutorial nicht?
  3. Warum behebt das Festlegen der Baudrate auf 2500 das Problem? (Ich vermute, dies wird beantwortet, wenn Frage 1 beantwortet wird)
Das einfache Definieren von F_CPU auf einen bestimmten Wert führt nicht dazu, dass der Mikro mit dieser Frequenz läuft. F_CPU sollte als die Frequenz definiert werden, mit der Sie das Mikro so konfiguriert haben, dass es läuft - aber ich sehe keine Beweise dafür, dass Sie dies irgendwo konfiguriert haben ...
Gut geschriebene Frage. Das einzige, was es verbessern würde, wäre ein Schaltplan.
+1 nur für die L EIN T E X Tisch.
Mir ist aufgefallen, dass Sie keinen externen Kristall auf Ihrem Steckbrett haben. Verwenden Sie die interne RC-Uhr? Mit welcher Frequenz soll der Prozessor laufen?
Dank Ihrer Diskussion über F_CPU habe ich etwas nachgeforscht und herumgespielt und die Lösung gepostet. Ich kann mir vorstellen, dass es für Sie offensichtlich ist (so wie für mich jetzt ), aber es könnte jemand anderem helfen.

Antworten (1)

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. Durch Überprüfen der Sicherungen in Atmel Studio konnte ich sehen, dass ich mit 2 MHz lief (dies ist meines Wissens nicht die Standardtaktfrequenz, aber ich werde nicht darauf eingehen) und nicht 7,3728 MHz wie im Tutorial.

F_CPU ändert nicht die Taktfrequenz der MCU (der Atmega16). Die Frequenz des Atmega16 wurde nicht auf 7,3728 MHz geändert, wie es notwendig war, um das Codebeispiel zum Laufen zu bringen. Es lief immer noch mit der durch die 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 im Vergleich zu dem, was der Atmega tatsächlich tat

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

Gleichungstabelle zur Berechnung von Baudrate und UBRR aus Seite 147 des Atmega16-Datenblatts

Im Codebeispiel BAUD_PRESCALEgeht UBRR in die Berechnung ein. wird als 47 mit den für und BAUD_PRESCALEdefinierten Werten ausgewertet .F_CPUUSART_BAUDRATE

BAUD = f Ö s c 16 ( UBRR + 1 )
BAUD = 2 , 000 , 000 16 ( 47 + 1 )
BAUD 2 , 604

Und das war die Wurzel des Problems. Der Atmega16 wurde mit 2 MHz betrieben, was bedeutete, dass der Wert von f_{osc} anders war als im Tutorial-Beispiel, was zu einer Baudrate von 2.604 im Gegensatz zu 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 3. Frage beantwortet: Das Ändern der Baudrate auf 2.500 war zum Glück nahe genug an der Betriebsbaudrate der MCU, dass das Terminal die Ergebnisse richtig interpretieren konnte.

Ändern der Frequenz der MCU

Um die Frequenz der MCU in AtmelStudio 7 zu ändern, gehen Sie zu:

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 standardmäßige interne Taktfrequenz, daher bleibe ich bei 2 MHz.

Zusammenfassung der Antworten auf meine eigenen Fragen

  1. Was habe ich falsch gemacht/was ist hier los? Antwort : Ich habe die Taktfrequenz nicht tatsächlich auf die Taktfrequenz im Tutorial geändert, was zu einer anderen Baudrate als erwartet führte, wodurch die Terminalsoftware (PuTTY) nicht mehr synchron mit der MCU war
  2. Warum #define F_CPU im ursprünglichen Tutorial nicht? Antwort : Ich bin mir 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 eine Zahl nahe der Baudrate des Atmega16 erraten