La salida en serie devuelve ASCII incorrecto

Estoy usando un cable FTDI y lo conecté con mi mac. Puedo conectarme con éxito con mi terminal serial en mi mac a través del cable y puedo ingresar texto en mi teclado para transmitirlo al AVR. Cuando se inicia el programa, espero que aparezca un mensaje "Hello World" en mi terminal serie. Pero en cambio, recibo este resultado en la pantalla:

ingrese la descripción de la imagen aquíLos ajustes del terminal soningrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquíingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

El código es este:

// ------- 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;
}

El archivo USART.c contiene:

#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');
}
Verifique que tenga tasas de baudios coincidentes en ambos extremos y que la frecuencia del reloj AVR sea la esperada.
tal vez una tasa de baudios no es igual?
La velocidad en baudios en el terminal serial está configurada en 9600. ¿Pensé que 9600 también está configurado en el código?
Verifique los valores de las variables UBBRH_VALUE y UBBRL_VALUE y asegúrese de que tengan los valores correctos para 9600 baudios. Además, la frecuencia del cristal es muy importante en la transmisión UART.
¿Podría dar más detalles sobre qué evidencia realmente confirma que "puedo conectarme con éxito con mi terminal serie en mi mac a través del cable..."? ¿Ha intentado un bucle invertido con el cable FTDI (Cortocircuito de los pines 2-3 (TX/RX) con un bolígrafo, por ejemplo, y escribiendo)?
No uso un cristal externo. ¿Quieres decir que? Entonces, ¿cómo configuro correctamente el valor UBBRH y UBBRL?
¿Has leído los comentarios del código? "UBRR0H = UBRRH_VALUE; /* definido en setbaud.h */"
@PauComaRamirez Sí, probé el loopback y funciona. Oh... interesante... cuando retrocedo, se muestran los caracteres correctos. Solo cuando me conecto con el AVR obtengo este extraño retorno de salida.
A mí me parece un vómito de baudios.
Aquí hay un ejemplo de configuración de BAUD de AVR: pastebin.com/L05SYB2C . Todavía se recomienda leer la parte relevante de la hoja de datos y los comentarios en el encabezado para entenderlo correctamente.
Su tasa de baudios es demasiado alta en un extremo, está interpretando 43 caracteres en lugar de 11 ... por lo tanto, un factor de aproximadamente 4 ... si 9k6 está en un lado, intente 38k4 en su lugar
Sí, leí eso. Cuando entro en el archivo setbaud.h encuentro las definiciones pero ningún valor
@PauComaRamirez ¿Cómo te diste cuenta?
Contando: "Hola mundo" --> 11 caracteres , "..ÄÄÄ.Ä.ÄÄ.ÄÄÄÄÄ..ÄÄÄÄÄÄÄ..Ä.ÄÄ..ÄÄ..ÄÄ..Ä." --> 43 caracteres
Lo siento, pero no entiendo cómo configuré correctamente mi tasa de baudios en mi código. ¿Cómo necesito reescribir el código correctamente para tener 9600? Agregué #define BAUD 9600en la parte superior de USART.c
No he revisado su código en detalle, pero diría que su código AVR en realidad se envía a 9k6, pero su terminal CoolTerm está configurado a 38k4. Cambia la configuración de la Terminal. ¿Cuáles son realmente los ajustes de la Terminal?
Agregué las capturas de pantalla de configuración de termin a mi pregunta.
intente poner un retraso de unos 10 segundos después de la cadena de impresión para ver si no es realmente su ciclo while infinito el que causa el problema
Ah, sí, comprueba la configuración de tus fusibles, ¿qué chip estás usando?

Respuestas (3)

Me encontré con este mismo problema, y ​​la respuesta proporcionada por @bence_kaulics es lo que me ayudó a superarlo, con un punto adicional:

Estoy en la misma situación que @secs360:

  1. atmega328p
  2. trabajando en el capítulo 5 (USART) del libro Make AVR Programming (la fuente del código de muestra proporcionado por @secs360)
  3. Puedo programar mi chip (las pruebas de parpadeo funcionan), pero el circuito de retroalimentación en serie responde con caracteres incorrectos. Varias combinaciones de configuraciones de BAUD en el código o en el terminal serial no resuelven el problema.

Pasos para arreglar:

Primero, confirme que he configurado el reloj correctamente:

Fusibles correctos (E:FF, H:D9, L:62)

Al compararlos con una calculadora de fusibles , veo que son los valores predeterminados: la MCU está configurada para usar el oscilador RC interno a 1 MHz.

Esto significa que debo configurar la velocidad de la CPU (en el archivo MAKE para los ejercicios del capítulo en este caso):

F_CPU = 1000000UL

También puedo configurar el valor BAUD en la misma ubicación. 9600 debería funcionar:

BAUD  = 9600UL

Hasta ahora, todo bien. Sin embargo, el libro usa una fórmula diferente para calcular los valores del registro UBRRn. @bence_kaulics proporciona la fórmula, de la hoja de datos.

(¿Tal vez la diferencia se deba a que el libro se escribió para chips atmega168? No lo sé. Pero cualquiera que sea la fuente, necesitamos usar el valor correcto aquí).

¡Hay un dato más! Si queremos usar 9600 BAUD, tendremos un error de -7% con velocidad de transmisión estándar, según la hoja de datos. Si duplicamos la velocidad de transmisión, nuestro error baja al 0,2%. Para hacer esto, no usamos la fórmula provista por @bence_kaulics, sino que usamos ((F_CPU)/(BAUD*8UL)-1)y configuramos el U2X0bit.

Lo hice modificando la initUSARTfunción en el archivo USART.c:

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 */
}

La versión original del libro usa la lógica en el archivo setbaud.h para determinar si duplicar o no la velocidad de transmisión. No lo entiendo todo, por lo que no estoy seguro de si el problema es la fórmula utilizada para BAUDRATE, o USE_2X, o para ambos. Sea lo que sea, el código anterior finalmente hizo que mi atmega328p hablara correctamente a través de la interfaz serial.

Sé que esto es viejo, pero gracias, Tyler. Tengo exactamente el mismo libro y estaba luchando con esto.

El primer paso debe ser verificar la configuración de su reloj. Usted dijo que no hay cristal externo, y su código definitivamente se ejecuta ahora, por lo que los bits del fusible CKSEL [3: 0] son ​​ciertamente 0010, el oscilador RC interno está seleccionado. Tablas tomadas de la hoja de datos .

![ingrese la descripción de la imagen aquí

Ahora, revisa también el bit del fusible CKDIV8, para saber si el oscilador RC interno está dividido por 8 o no.

ingrese la descripción de la imagen aquí

Está programado de forma predeterminada, por lo que si nunca ha tocado los bits del fusible, su MCU probablemente funcione en 1 MHz.

Si CKDIV8 es 0, defina la frecuencia de su CPU de la siguiente manera:

#define  F_CPU 1000000UL

y si es 1, entonces:

#define  F_CPU 8000000UL

El siguiente paso es calcular el valor del registro de tasa BAUD, que se puede hacer con las siguientes macros.

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

Esto le dará el valor correcto en BAUDRATE. La ecuación se da en la hoja de datos.

![ingrese la descripción de la imagen aquí

En su función de inicio de UART, pase esto a los UBBRHregistros UBBRL.

UBRR0H = (BAUDRATE>>8);  // shift the register right by 8 bits to get the upper 8 bits
UBRR0L = BAUDRATE;       // set baud rate
Acabo de pedir otro cable FTDI (esta vez no de eBay sino de Adafruit). vamos a ver.
@sec360 podría serlo, pero dudo que el cable FTDI tenga la culpa. Si planea hacer más proyectos, invertiría en un analizador lógico/de alcance. Puede obtener uno barato en eBay, pero recomendaría uno de saleae.com , tengo uno y me ha ayudado mucho en la depuración.
Tengo una copia saleae de ebay, me ha servido bien hasta ahora.

La solución será cualquiera de las siguientes

  1. Verifique la frecuencia del reloj de la placa AVR, debe ser el valor que usted toma para calcular la tasa de baudios
  2. Compruebe los valores del registro de velocidad en baudios (UBRR), debe ser 129 (en decimal) para una velocidad de 9600 baudios a una frecuencia de reloj de 20 MHz. Además de verificar con la fórmula disponible en la hoja de datos
  3. Compruebe el cable RS232 (menos posibilidades) y el convertidor (MAX 232 o cualquier otro)