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 hacer que Atmega16 envíe de vuelta cualquier personaje 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 de transmisión, pero no entiendo por qué funciona la velocidad de transmisión correcta.

Mas detalle

Estoy tratando de aprender más sobre programación de firmware en mi propio tiempo porque lo disfruto bastante. Hasta ahora, en la programación de firmware que he realizado en uni, se nos han proporcionado archivos de código de esqueleto que realizan muchas de las interfaces y configuraciones periféricas, pero me gustaría aprender esto yo mismo. Tengo algunas preguntas sobre lo que estoy haciendo aquí esparcidas por toda la publicación, pero las detallaré al final. Si conoces cualquier malentendido o posibles lagunas en mi conocimiento, agradecería mucho cualquier comentario que puedas tener.

El código

El código que mostré en mi Atmega16 se toma casi línea por línea del tutorial 'Usando el USART en AVR-GCC' que se encuentra en esta página . Todo lo que he agregado 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 pueden haber estado usando alguna otra herramienta o compilador que no sea Atmel Studio 7, pero no puedo decirlo 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 de hardware

Foto de la configuración del hardware

  • MCU: Atmega16;
  • Cadena de herramientas: Atmel Studio 7, que parpadea con el dragón AVR;
  • Fuente de alimentación: carril de 5V tomado de una placa de desarrollo proporcionada por la universidad (que se toma del USB de la computadora) Condensador de disco de cerámica de 100nF utilizado para evitar las líneas eléctricas de la placa de pruebas
  • Convertidor de USB a serie: este . 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 velocidad de transmisión de 9600).

    Evidencia de respuestas incorrectas

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

    Salida PuTTY

    ENTRADASALIDAFYF6 6z>re0 0espacio0 0X8

    Capturas de osciloscopio

    He usado mi Picoscope con decodificación en serie para verificar que 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 ampersand '&' en ocasiones).

Captura del alcance en el pin RX del Atmega16 que muestra que se está enviando el carácter correcto a través del software del terminal ('f')

Captura del alcance en el pin TX del Atmega16 que muestra que se está enviando una respuesta no deseada ('6')

Me encontré con una solución que no entiendo

Si cambio la velocidad de transmisión a 2500in PuTTY, todo se muestra correctamente. Elegí este valor al azar y no sé por qué funciona (me lleva a creer que he cometido un error en algún lugar relacionado con la velocidad en baudios, pero no veo dónde, 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é la configuración de la velocidad en baudios a 2500 soluciona el problema? (Sospecho que esto se responderá si se responde la pregunta 1)
daviegravee
fuente
2
Simplemente definir F_CPU a algún valor no hace que el micro funcione a esa frecuencia. F_CPU debería definirse como la frecuencia a la que usted tiene configure el micro para correr - pero no ve ninguna evidencia de que haya configurado en cualquier lugar ...
brhans
Pregunta bien escrita Lo único que lo mejoraría sería un esquema.
Blair Fonville el
+1 solo por LUNATmiXmesa.
Arsenal
Me doy cuenta de que no tienes cristal externo en tu placa de pruebas. ¿Estás usando el reloj RC interno? ¿A qué frecuencia espera que se ejecute el procesador?
scotty3785
Gracias a su discusión sobre F_CPU, investigué y jugué un poco y publiqué la solución. Me imagino que es obvio para usted (como lo es para mí ahora ) pero podría ayudar a alguien más.
daviegravee

Respuestas:

0

Lo he descubierto! Gracias a los comentarios sobre F_CPU en respuesta al OP, investigué un poco (esto podría ser obvio para todos ustedes).

Breve resumen de la solución

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

F_CPU no cambia la frecuencia de reloj de la MCU (Atmega16). La frecuencia del Atmega16 no se cambió a 7.3728MHz como era necesario para que el ejemplo de código funcionara. Seguía funcionando a la frecuencia definida por los fusibles (2MHz en este caso, más sobre esto a continuación), por lo que el cálculo en papel de la velocidad de transmisión deseada 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
    }
}

Mas detalle

Baudrate deseado frente a lo que realmente estaba haciendo el Atmega

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

Tabla de ecuaciones para calcular la velocidad en baudios y la UBRR a partir de la página 147 Hoja de datos Atmega16

En el código de ejemplo BAUD_PRESCALEes UBRR en el cálculo. BAUD_PRESCALEse evalúa como 47 con los valores definidos para F_CPUy USART_BAUDRATE.

Baudios=FosCdieciséis(UBRR+1)
Baudios=2,000,000dieciséis(47+1)
Baudios2,604

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

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

Eso también responde a mi tercera pregunta: cambiar la velocidad de transmisión a 2.500 fue, afortunadamente, lo suficientemente cerca de la velocidad de transmisión operativa de la MCU como para que el terminal pudiera interpretar correctamente los resultados.

Cambiar 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 interna estándar, por lo que me quedaré 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 de transmisión diferente a la esperada, lo que dejó el software del terminal (PuTTY) fuera de sincronización con el MCU
  2. ¿Por qué el tutorial original no #define F_CPU? Respuesta : Todavía no estoy del todo seguro, pero supongo que se define en un archivo MAKE que no se proporciona en el tutorial y que el autor no estaba utilizando un IDE como Atmel Studio
  3. ¿Por qué la configuración de la velocidad en baudios a 2500 soluciona el problema? (Sospecho que esto se responderá si se responde la pregunta 1) Respuesta : Afortunadamente adiviné un número cercano al baudrate del Atmega16
daviegravee
fuente