La salida en serie devuelve un ASCII incorrecto

3

Estoy usando un cable FTDI y lo conecté con mi mac. Puedo conectarme exitosamente con mi terminal serial en mi mac a través del cable y puedo ingresar texto en mi teclado para ser transmitido al AVR. Cuando se inicie el programa, espero que aparezca un mensaje "Hello World" en mi terminal serial. Pero en cambio recibo esta salida en la pantalla:

Losajustesdelterminalson

introduceladescripcióndelaimagenaquí

Elcódigoeseste:

//-------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');
}
    
pregunta sesc360

3 respuestas

5

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

Estoy en la misma situación que @ secs360:

  1. atmega328p
  2. trabajando a través del capítulo 5 (USART) en el libro Make AVR Programming (la fuente del ejemplo de código proporcionado por @ secs360)
  3. Puedo programar mi chip (las pruebas de parpadeo funcionan), pero el bucle de respuesta 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 corregir:

Primero, confirma que he puesto el reloj correctamente:

  

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

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

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

F_CPU = 1000000UL

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

BAUD  = 9600UL

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

(¿Quizás la diferencia se deba a que el libro está escrito para los chips atmega168? No lo sé. Pero sea cual sea la fuente, necesitamos usar el valor correcto aquí).

¡Hay una información más! Si queremos usar 9600 BAUD, tendremos un error de -7% con la velocidad de transmisión estándar, de acuerdo con la hoja de datos. Si duplicamos la velocidad de transmisión, nuestro error cae a 0.2%. Para hacer esto, no usamos la fórmula proporcionada por @bence_kaulics, sino que usamos ((F_CPU)/(BAUD*8UL)-1) y configuramos el bit U2X0 .

Lo hice modificando la función initUSART 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 utiliza la lógica en el archivo setbaud.h para determinar si se debe duplicar o no la velocidad de transmisión. No entiendo todo esto, por lo que no estoy seguro de si el problema es la fórmula utilizada para el BAUDRATE , o USE_2X , o ambos. Sea lo que sea, el código anterior ha conseguido que mi atmega328p funcione correctamente a través de la interfaz serial.

    
respondido por el Tyler
6

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

Ahora,compruebetambiénelbitdefusibleCKDIV8,parasabersielosciladorRCinternoestádivididopor8ono.

Estáprogramadodeformapredeterminada,porloquesinuncahatocadolosbitsdefusible,suMCUprobablementeseejecutaen1MHz.

SielCKDIV8es0,definalafrecuenciadesuCPUdelasiguientemanera:

#defineF_CPU1000000UL

ysies1,entonces:

#defineF_CPU8000000UL

ElsiguientepasoescalcularelvalordelregistrodevelocidaddeBAUD,quesepuedehacerconlassiguientesmacros.

#defineBAUD9600//desiredbaud#defineBAUDRATE((F_CPU)/(BAUD*16UL)-1)//setbaudratevalueforUBRR

EstoledaráelvalorcorrectoenBAUDRATE.Laecuaciónsedaenlahojadedatos.

En la función de inicio de UART, pase esto a los registros UBBRH y UBBRL .

UBRR0H = (BAUDRATE>>8);  // shift the register right by 8 bits to get the upper 8 bits
UBRR0L = BAUDRATE;       // set baud rate
    
respondido por el Bence Kaulics
4

La solución será cualquiera de las siguientes

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

Lea otras preguntas en las etiquetas