Respuesta inesperada de Atmega16 sobre UART

8

Respuesta inesperada de Atmega16 sobre UART

Breve resumen del problema

He flasheado un Atmega16 con un código que debería dar como resultado que el Atmega16 devuelva cualquier carácter que le envíe a través de un terminal. Recibo una respuesta, pero rara vez es el personaje que envié. Puedo ver la salida correcta cambiando la velocidad en baudios pero no entiendo por qué funciona la velocidad en baudios correcta.

Más detalles

Estoy intentando aprender más sobre la programación de firmware en mi propio tiempo porque lo disfruto bastante. Hasta ahora, en la programación de firmware que he hecho en la universidad, nos han proporcionado archivos de código de esqueleto que hacen una gran parte de la interfaz periférica y se configuran para nosotros, pero me gustaría aprender esto por mí mismo. Tengo algunas preguntas sobre lo que estoy haciendo aquí repartidas a lo largo del post, pero las detallaré al final. Si se entera de cualquier malentendido o posible brecha en mi conocimiento, agradecería enormemente cualquier información que pueda tener.

El código

El código que he flasheado en mi Atmega16 se toma casi línea por línea del tutorial 'Usando el USART en AVR-GCC' encontrado en esta página . Todo lo que he añadido es el #define para F_CPU. El código original no tenía un #define para F_CPU por lo que mi código no se compilaría en AtmelStudio 7. ¿Alguien podría explicar por qué el autor no habría definido F_CPU en su archivo original? Supongo que es posible que hayan estado utilizando alguna otra herramienta o compilador que Atmel Studio 7, pero no puedo decir con certeza.

#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
    }
}

La configuración del hardware

  • MCU:Atmega16;
  • Cadenadeherramientas:AtmelStudio7,parpadeandoconAVRdragon;
  • Fuentedealimentación:rielde5Vtomadodeunaplacadedesarrolloprovistaporlauniversidad(quesetomadesdeunacomputadoraUSB).Condensadordediscocerámicode100nFutilizadoparadesviarlaslíneaseléctricasdelaplacadepruebas
  • convertidordeUSBaserie: Éste . TXD en el convertidor de USB a serie conectado a RXD Atmega (Pin 15). RXD en el convertidor conectado a RXD en Atmega (Pin 14).
  • Software de terminal: PuTTY (con una velocidad en baudios de 9600).

    Evidencia de las respuestas incorrectas

    Para reiterar, el Atmega debería devolver lo que se le envió, es decir, que OUTPUT debería ser exactamente igual a INPUT.

    salida PuTTY

    \ begin {array} \ hline \ text {ENTRADA} & \ text {SALIDA} \\ \ hline  \ text {f} & \ text {&} \\ \ hline  \ text {f} & \ text {6} \\ \ hline  \ text {z} & \ text {>} \\ \ hline  \ text {d} & \ text {0} \\ \ hline  \ text {espacio} & \ text {0} \\ \ hline \ text {x} & \ text {8} \\ \ hline \ end {array}

    Capturas de osciloscopios

    He usado mi Picoscope con decodificación en serie para verificar que el Atmega esté recibiendo la entrada correcta, que parece ser. Por ejemplo, cuando presiono la tecla 'f', se recibe correctamente. La salida sigue siendo un '6' (o un símbolo '&' en ocasiones).

Una solución que encontré y que no entiendo

Si cambio la velocidad en baudios a 2500 en PuTTY, todo se muestra correctamente. Elegí este valor al azar y no sé por qué funciona (me hace creer que he cometido un error en algún lugar relacionado con la velocidad en baudios, pero no veo dónde dado copié el tutorial casi exactamente ... pensamiento).

Preguntas

  1. ¿Qué he hecho mal / qué está pasando aquí?
  2. ¿Por qué el tutorial original no # define F_CPU?
  3. ¿Por qué establecer la velocidad en baudios en 2500 soluciona el problema? (Sospecho que esto será respondido si se responde la pregunta 1)
pregunta daviegravee

1 respuesta

0

Lo he descubierto! Gracias a los comentarios sobre F_CPU en respuesta al OP, hice algunas investigaciones (esto podría ser obvio para todos).

Breve resumen de la solución

El Atmega16 no se estaba ejecutando a la frecuencia que pensé que era porque no entendía cómo cambiar la frecuencia de su sistema. Al registrar los fusibles en Atmel Studio, pude ver que estaba funcionando a 2MHz (esta no es la frecuencia de reloj estándar, por lo que sé, pero no entraré en eso), y no 7.3728MHz como el tutorial.

F_CPU no cambia la frecuencia de reloj de la MCU (el Atmega16). La frecuencia del Atmega16 se no se cambió a 7.3728MHz como fue necesario para que el código de ejemplo funcione. Todavía funcionaba a la frecuencia definida por los fusibles (2 MHz en este caso, más sobre esto más adelante), por lo que el cálculo en papel del deseado velocidad en baudios difiere de lo que realmente se usó.

Código de trabajo

#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
    }
}

Más detalles

Velocidad de transmisión deseada frente a lo que el Atmega estaba en realidad haciendo

La velocidad en baudios deseada (del tutorial) fue 9600, que es la velocidad en baudios que usé en PuTTY. La velocidad en baudios real se puede calcular utilizando la ecuación resaltada en la Tabla 60 (página 147) de la hoja de datos de Atmega16.

En el ejemplo de código BAUD_PRESCALE es UBRR en el cálculo. BAUD_PRESCALE se evalúa como 47 con los valores definidos para F_CPU y USART_BAUDRATE .

$$ \ text {BAUD} = \ frac {f_ {osc}} {16 (\ text {UBRR} + 1)} $$ $$ \ text {BAUD} = \ frac {2,000,000} {16 (\ text {47} + 1)} $$ $$ \ text {BAUD} \ approx 2,604 $$

Y esta fue la raíz del problema. El Atmega16 operaba a 2MHz, lo que significaba que el valor de f_ {osc} era diferente al ejemplo tutorial, que resultó en una tasa de baudios de 2.604 en comparación con 9.600.

Observe que f_osc es la frecuencia del sistema actual de la MCU, que no está determinada por F_CPU .

Eso también responde a mi tercera pregunta: cambiar la velocidad en baudios a 2,500 fue afortunadamente lo suficientemente cerca de la velocidad en baudios de funcionamiento de la MCU para que el terminal pudiera interpretar correctamente los resultados.

Cambiando la frecuencia de la MCU

Para cambiar la frecuencia de la MCU en AtmelStudio 7, vaya:

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). 

La frecuencia utilizada en el ejemplo no es una frecuencia de reloj interno estándar, así que me quedo con 2MHz.

Resumen de respuestas a mis propias preguntas

  1. ¿Qué he hecho mal / qué está pasando aquí? Respuesta : en realidad, no cambió la frecuencia del reloj a la frecuencia del reloj en el tutorial, lo que resultó en una velocidad en baudios diferente a la que se esperaba, lo que puso el software del terminal (PuTTY) fuera de sincronización con la MCU
  2. ¿Por qué el tutorial original no # define F_CPU? Respuesta : aún no estoy completamente seguro, pero supongo que está definido en un archivo make que no se encuentra en el tutorial y que el autor no estaba haciendo uso de un IDE como Atmel Studio
  3. ¿Por qué establecer la velocidad en baudios en 2500 soluciona el problema? (Sospecho que esto se responderá si se responde a la pregunta 1) Respuesta : por suerte adiviné un número cercano a la velocidad en baudios del Atmega16
respondido por el daviegravee

Lea otras preguntas en las etiquetas