Serielle Ausgabe gibt falsches ASCII zurück

Ich verwende ein FTDI-Kabel und verbinde es mit meinem Mac. Ich kann mich über das Kabel erfolgreich mit meinem seriellen Terminal auf meinem Mac verbinden und Text auf meiner Tastatur eingeben, der an den AVR übertragen werden soll. Wenn das Programm startet, erwarte ich, dass eine "Hello World"-Meldung auf meinem seriellen Terminal erscheint. Aber stattdessen erhalte ich diese Ausgabe auf dem Bildschirm:

Geben Sie hier die Bildbeschreibung einDie Terminaleinstellungen sindGeben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung einGeben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Der Code ist dieser:

// ------- Preamble -------- //
#include <avr/io.h>
#include <util/delay.h>
#include "pinDefines.h"
#include "USART.h"

int main(void) {
  char serialCharacter;

  // -------- Inits --------- //
  LED_DDR = 0xff;                            /* set up LEDs for output */
  initUSART();
  printString("Hello World!\r\n");                          /* to test */

  // ------ Event loop ------ //
  while (1) {

    serialCharacter = receiveByte();
    transmitByte(serialCharacter);
    LED_PORT = serialCharacter;
                           /* display ascii/numeric value of character */

  }                                                  /* End event loop */
  return 0;
}

Die USART.c-Datei enthält:

#include <avr/io.h>
#include "USART.h"
#include <util/setbaud.h>
#define BAUD 9600

void initUSART(void) {                                /* requires BAUD */
  UBRR0H = UBRRH_VALUE;                        /* defined in setbaud.h */
  UBRR0L = UBRRL_VALUE;
#if USE_2X
  UCSR0A |= (1 << U2X0);
#else
  UCSR0A &= ~(1 << U2X0);
#endif
                                  /* Enable USART transmitter/receiver */
  UCSR0B = (1 << TXEN0) | (1 << RXEN0);
  UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);   /* 8 data bits, 1 stop bit */
}

void transmitByte(uint8_t data) {
                                     /* Wait for empty transmit buffer */
  loop_until_bit_is_set(UCSR0A, UDRE0);
  UDR0 = data;                                            /* send data */
}

uint8_t receiveByte(void) {
  loop_until_bit_is_set(UCSR0A, RXC0);       /* Wait for incoming data */
  return UDR0;                                /* return register value */
}

                       /* Here are a bunch of useful printing commands */

void printString(const char myString[]) {
  uint8_t i = 0;
  while (myString[i]) {
    transmitByte(myString[i]);
    i++;
  }
}

void readString(char myString[], uint8_t maxLength) {
  char response;
  uint8_t i;
  i = 0;
  while (i < (maxLength - 1)) {                   /* prevent over-runs */
    response = receiveByte();
    transmitByte(response);                                    /* echo */
    if (response == '\r') {                     /* enter marks the end */
      break;
    }
    else {
      myString[i] = response;                       /* add in a letter */
      i++;
    }
  }
  myString[i] = 0;                          /* terminal NULL character */
}

void printByte(uint8_t byte) {
              /* Converts a byte to a string of decimal text, sends it */
  transmitByte('0' + (byte / 100));                        /* Hundreds */
  transmitByte('0' + ((byte / 10) % 10));                      /* Tens */
  transmitByte('0' + (byte % 10));                             /* Ones */
}

void printWord(uint16_t word) {
  transmitByte('0' + (word / 10000));                 /* Ten-thousands */
  transmitByte('0' + ((word / 1000) % 10));               /* Thousands */
  transmitByte('0' + ((word / 100) % 10));                 /* Hundreds */
  transmitByte('0' + ((word / 10) % 10));                      /* Tens */
  transmitByte('0' + (word % 10));                             /* Ones */
}

void printBinaryByte(uint8_t byte) {
                       /* Prints out a byte as a series of 1's and 0's */
  uint8_t bit;
  for (bit = 7; bit < 255; bit--) {
    if (bit_is_set(byte, bit))
      transmitByte('1');
    else
      transmitByte('0');
  }
}

char nibbleToHexCharacter(uint8_t nibble) {
                                   /* Converts 4 bits into hexadecimal */
  if (nibble < 10) {
    return ('0' + nibble);
  }
  else {
    return ('A' + nibble - 10);
  }
}

void printHexByte(uint8_t byte) {
                        /* Prints a byte as its hexadecimal equivalent */
  uint8_t nibble;
  nibble = (byte & 0b11110000) >> 4;
  transmitByte(nibbleToHexCharacter(nibble));
  nibble = byte & 0b00001111;
  transmitByte(nibbleToHexCharacter(nibble));
}

uint8_t getNumber(void) {
  // Gets a numerical 0-255 from the serial port.
  // Converts from string to number.
  char hundreds = '0';
  char tens = '0';
  char ones = '0';
  char thisChar = '0';
  do {                                                   /* shift over */
    hundreds = tens;
    tens = ones;
    ones = thisChar;
    thisChar = receiveByte();                   /* get a new character */
    transmitByte(thisChar);                                    /* echo */
  } while (thisChar != '\r');                     /* until type return */
  return (100 * (hundreds - '0') + 10 * (tens - '0') + ones - '0');
}
Überprüfen Sie, ob Sie an beiden Enden übereinstimmende Baudraten haben und ob die AVR-Taktfrequenz wie erwartet ist.
vielleicht ist eine Baudrate nicht gleich?
Die Baudrate im seriellen Terminal ist auf 9600 eingestellt. Ich dachte, 9600 ist auch im Code eingestellt?
Überprüfen Sie die Werte der Variablen UBBRH_VALUE und UBBRL_VALUE und stellen Sie sicher, dass sie die richtigen Werte für 9600 Baud haben. Außerdem ist die Kristallfrequenz bei der UART-Übertragung sehr wichtig.
Könnten Sie erläutern, welche Beweise tatsächlich bestätigen, dass "ich über das Kabel erfolgreich eine Verbindung zu meinem seriellen Terminal auf meinem Mac herstellen kann ..."? Haben Sie einen Loopback mit dem FTDI-Kabel versucht (z. B. Pins 2-3 (TX/RX) mit einem Stift kurzschließen und tippen)?
Ich verwende keinen externen Kristall. Meinst du das? Wie stelle ich dann den UBBRH- und UBBRL-Wert richtig ein?
Hast du die Codekommentare gelesen? "UBRR0H = UBRRH_VALUE; /* definiert in setbaud.h */"
@PauComaRamirez Ja, ich habe den Loopback ausprobiert und es funktioniert. Oh ... interessant ... wenn ich zurückschleife, werden die richtigen Zeichen angezeigt. Nur wenn ich mich mit dem AVR verbinde, bekomme ich diese seltsame Ausgabe zurück.
Sieht für mich nach Baudkotz aus.
Hier ist ein Beispiel für die Einstellung von BAUD von AVR: pastebin.com/L05SYB2C . Es wird dennoch empfohlen, den relevanten Teil des Datenblatts und die Kommentare in der Kopfzeile zu lesen, um es richtig zu verstehen.
Ihre Baudrate ist an einem Ende zu hoch, Sie interpretieren 43 Zeichen statt 11.. also ein Faktor von etwa 4.. wenn 9k6 auf einer Seite ist, versuchen Sie stattdessen 38k4
Ja das habe ich gelesen. Wenn ich in die Datei setbaud.h gehe, finde ich die Definitionen, aber keinen Wert
@PauComaRamirez Wie hast du das herausgefunden?
Zählen: "Hallo Welt" --> 11 Zeichen , "..ÄÄÄ.Ä.ÄÄ.ÄÄÄÄÄ..ÄÄÄÄÄÄÄ..Ä.ÄÄ..ÄÄ..ÄÄ..Ä." --> 43 Zeichen
Es tut mir leid, aber ich verstehe nicht, wie ich meine Baudrate in meinem Code richtig einstelle. Wie muss ich den Code richtig umschreiben, um 9600 zu haben? Ich habe #define BAUD 9600oben in der USART.c hinzugefügt
Ich habe Ihren Code nicht im Detail überprüft, aber ich würde sagen, dass Ihr AVR-Code tatsächlich mit 9k6 sendet, Ihr CoolTerm-Terminal jedoch mit 38k4 konfiguriert ist. Ändern Sie die Einstellungen des Terminals. Wie sind eigentlich die Einstellungen des Terminals?
Ich habe meiner Frage die Screenshots der Termineinstellungen hinzugefügt.
Versuchen Sie, eine Verzögerung von etwa 10 Sekunden nach dem printString einzubauen, um zu sehen, ob es nicht tatsächlich Ihre Endlosschleife ist, die das Problem verursacht
Oh ja, überprüfen Sie Ihre Sicherungseinstellungen, welchen Chip verwenden Sie?

Antworten (3)

Ich bin auf dasselbe Problem gestoßen, und die Antwort von @bence_kaulics hat mich durchgebracht, mit einem zusätzlichen Punkt:

Ich bin in der gleichen Situation wie @secs360:

  1. atmega328p
  2. Durcharbeiten von Kapitel Kapitel 5 (USART) im Buch Make AVR Programming (die Quelle des Codebeispiels, bereitgestellt von @secs360)
  3. Ich kann meinen Chip programmieren (Blinktests funktionieren), aber die serielle Rückkopplungsschleife antwortet mit falschen Zeichen. Verschiedene Kombinationen von BAUD-Einstellungen im Code oder im seriellen Terminal lösen das Problem nicht.

Schritte zur Behebung:

Bestätigen Sie zuerst, dass ich die Uhr richtig eingestellt habe:

Sicherungen OK (E:FF, H:D9, L:62)

Wenn ich diese mit einem Sicherungsrechner vergleiche , sehe ich, dass es sich um die Standardwerte handelt: Die MCU ist so eingestellt, dass sie den internen RC-Oszillator bei 1 MHz verwendet.

Das heißt, ich sollte die CPU-Geschwindigkeit einstellen (in diesem Fall im Makefile für die Kapitelübungen):

F_CPU = 1000000UL

Ich kann auch den BAUD-Wert an derselben Stelle einstellen. 9600 sollte funktionieren:

BAUD  = 9600UL

So weit, ist es gut. Das Buch verwendet jedoch eine andere Formel zur Berechnung der UBRRn-Registerwerte. @bence_kaulics liefert die Formel aus dem Datenblatt.

(Vielleicht liegt der Unterschied daran, dass das Buch für atmega168-Chips geschrieben wurde? Ich weiß es nicht. Aber unabhängig von der Quelle müssen wir hier den richtigen Wert verwenden.)

Es gibt noch eine Information! Wenn wir 9600 BAUD verwenden wollen, haben wir laut Datenblatt bei Standard-Übertragungsgeschwindigkeit einen Fehler von -7%. Wenn wir die Übertragungsgeschwindigkeit verdoppeln, sinkt unser Fehler auf 0,2 %. Dazu verwenden wir nicht die von @bence_kaulics bereitgestellte Formel, sondern verwenden stattdessen ((F_CPU)/(BAUD*8UL)-1), und setzen das U2X0Bit.

Ich habe das getan, indem ich die initUSARTFunktion in der Datei USART.c geändert habe:

void initUSART(void) {                                /* requires BAUD */
    #define BAUDRATE ((F_CPU)/(BAUD*8UL)-1) // set baud rate value for UBRR
    UBRR0H = (BAUDRATE>>8);  // shift the register right by 8 bits to get the upper 8 bits
    UBRR0L = BAUDRATE;       // set baud rate

    UCSR0A |= (1 << U2X0);   // double transmission speed

                              /* Enable USART transmitter/receiver */
    UCSR0B = (1 << TXEN0) | (1 << RXEN0);
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);   /* 8 data bits, 1 stop bit */
}

Die Originalversion aus dem Buch verwendet Logik in der Datei setbaud.h, um zu bestimmen, ob die Übertragungsgeschwindigkeit verdoppelt werden soll oder nicht. Ich verstehe nicht alles und bin mir daher nicht sicher, ob das Problem an der Formel liegt, die für , BAUDRATEoder USE_2X, oder beide verwendet wird. Was auch immer es ist, der obige Code hat meinen atmega328p endlich dazu gebracht, richtig über die serielle Schnittstelle zu sprechen.

Ich weiß, das ist alt, aber danke Tyler! Ich habe genau das gleiche Buch und hatte damit zu kämpfen.

Der erste Schritt sollte die Überprüfung Ihrer Uhrenkonfiguration sein. Sie sagten, es gibt keinen externen Kristall, und Ihr Code wird jetzt definitiv ausgeführt, sodass die Sicherungsbits CKSEL [3: 0] sicher sind 0010, der interne RC-Oszillator ist ausgewählt. Tabellen aus dem Datenblatt .

![Bildbeschreibung hier eingeben

Überprüfen Sie nun auch das Fuse-Bit CKDIV8, um zu wissen, ob der interne RC-Oszillator durch 8 geteilt ist oder nicht.

Geben Sie hier die Bildbeschreibung ein

Es ist standardmäßig programmiert. Wenn Sie also noch nie die Sicherungsbits berührt haben, läuft Ihre MCU wahrscheinlich mit 1 MHz.

Wenn CKDIV8 0 ist, definieren Sie Ihre CPU-Frequenz wie folgt:

#define  F_CPU 1000000UL

und wenn es 1 ist, dann:

#define  F_CPU 8000000UL

Der nächste Schritt ist die Berechnung des Wertes des BAUD-Ratenregisters, was mit den folgenden Makros erfolgen kann.

#define BAUD 9600                        // desired baud
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1) // set baud rate value for UBRR

Dadurch erhalten Sie den korrekten Wert in BAUDRATE. Gleichung ist im Datenblatt angegeben.

![Bildbeschreibung hier eingeben

Übergeben Sie dies in Ihrer UART-Init-Funktion an die Register UBBRHund UBBRL.

UBRR0H = (BAUDRATE>>8);  // shift the register right by 8 bits to get the upper 8 bits
UBRR0L = BAUDRATE;       // set baud rate
Ich habe gerade ein weiteres FTDI-Kabel bestellt (diesmal nicht bei ebay, sondern bei Adafruit). mal sehen.
@sec360 könnte sein, aber ich bezweifle, dass das FTDI-Kabel schuld ist. Wenn Sie weitere Projekte planen, würde ich in einen Bereichs- / Logikanalysator investieren. Sie können einen billig bei ebay bekommen, aber ich würde einen von saleae.com empfehlen , ich habe einen und er hat mir beim Debuggen sehr geholfen.
Ich habe Saleae-Kopie von ebay, hat mir bisher gute Dienste geleistet.

Die Lösung wird eine der folgenden sein

  1. Überprüfen Sie die Taktfrequenz der AVR-Karte, es sollte der Wert sein, den Sie zur Berechnung der Baudrate verwenden
  2. Überprüfen Sie die Werte des Baudratenregisters (UBRR), es sollte 129 (dezimal) für eine Baudrate von 9600 bei einer Taktfrequenz von 20 MHz sein. Sowie Überprüfung mit der im Datenblatt verfügbaren Formel
  3. Überprüfen Sie das RS232-Kabel (geringere Wahrscheinlichkeit) und den Konverter (MAX 232 oder einen anderen).