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
- 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
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 '&').
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
- Was habe ich falsch gemacht / was passiert hier?
- Warum definiert das ursprüngliche Tutorial F_CPU nicht #?
- Warum behebt das Festlegen der Baudrate auf 2500 das Problem? (Ich vermute, dass dies beantwortet wird, wenn Frage 1 beantwortet wird)
Antworten:
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
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.
Im Codebeispiel
BAUD_PRESCALE
ist UBRR in der Berechnung.BAUD_PRESCALE
wird mit den fürF_CPU
und definierten Werten als 47 bewertetUSART_BAUDRATE
.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:
Die im Beispiel verwendete Frequenz ist keine interne Standardtaktfrequenz, daher bleibe ich bei 2 MHz.
Zusammenfassung der Antworten auf meine eigenen Fragen
quelle