Respuesta inesperada de Atmega16 a través de UART
He mostrado un Atmega16 con un código que debería hacer que el Atmega16 devuelva cualquier carácter que le envíe a través de una terminal. Recibo una respuesta, pero rara vez es el carácter que envié. Puedo ver la salida correcta cambiando la velocidad en baudios, pero no entiendo por qué funciona la velocidad en baudios correcta.
Estoy tratando de 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 dado archivos de código de esqueleto que hacen muchas de las interfaces periféricas y las configuran para nosotros, pero me gustaría aprender esto yo mismo. Tengo algunas preguntas sobre lo que estoy haciendo aquí esparcidas a lo largo de la publicación, pero las detallaré al final. Si detecta algún malentendido o posibles lagunas en mi conocimiento, agradecería mucho cualquier aporte que pueda tener.
El código que he instalado en mi Atmega16 se toma casi línea por línea del tutorial 'Uso del USART en AVR-GCC' que se encuentra en esta página . Todo lo que he agregado es #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
}
}
Software de terminal: PuTTY (con baudrate de 9600).
Para reiterar, el Atmega debe devolver lo que se le envió, es decir, la SALIDA debe ser exactamente igual a la ENTRADA.
He usado mi Picoscope con decodificación en serie para verificar que el Atmega esté recibiendo la entrada correcta, lo que parece ser. Por ejemplo, cuando presiono la tecla 'f', se recibe correctamente. La salida sigue siendo un '6' (o un ampersand '&' en ocasiones).
Si cambio la tasa de baudios a 2500 en PuTTY, todo se muestra correctamente. Elegí este valor al azar y no sé por qué funciona (me lleva a creer que cometí un error en alguna parte relacionado con la velocidad en baudios, pero no veo dónde, copié el tutorial casi exactamente ... I pensamiento).
¡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).
El Atmega16 no funcionaba a la frecuencia que pensaba 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 2 MHz (que yo sepa, esta no es la frecuencia de reloj estándar, pero no entraré en eso), y no a 7.3728 MHz como en el tutorial.
F_CPU no cambia la frecuencia de reloj de la MCU (Atmega16). La frecuencia del Atmega16 no se cambió a 7,3728 MHz, ya que era necesario para que el código de ejemplo funcionara. Todavía estaba funcionando a la frecuencia definida por los fusibles (2 MHz en este caso, más sobre esto a continuación), por lo que el cálculo en papel de la tasa de baudios deseada difiere de lo que realmente se usó.
#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
}
}
La tasa de baudios deseada (del tutorial) era 9600, que es la tasa de baudios que usé en PuTTY. La tasa de baudios real se puede calcular usando 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
.
Y esta era la raíz del problema. El Atmega16 funcionaba a 2 MHz, lo que significaba que el valor de f_{osc} era diferente al ejemplo del tutorial, lo que resultó en una velocidad de transmisión de 2604 en lugar de 9600.
Tenga en cuenta 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 en baudios a 2500 fue lo suficientemente cercano a la velocidad en baudios operativa de la MCU para que la terminal pudiera interpretar correctamente los resultados.
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, por lo que me quedaré con 2MHz.
brahans
Blair Fonville
Arsenal
escocés3785
daviegravee