Unexpected Atmega16 response over UART
Brief problem summary
I have flashed an Atmega16 with code that should result in the Atmega16 sending back whatever character I send to it via a terminal. I get a response, but it is rarely ever the character I sent. I can see correct output by changing the baud rate but I don't understand why the correct baud rate works.
More detail
I am trying to learn more about firmware programming in my own time because I quite enjoy it. Thus far in the firmware programming I have done at uni, we have been given skeleton code files that do a lot of the peripheral interfacing and set up for us, but I would like to learn this myself. I've got a few questions about what i'm doing here sprinkled throughout the post but I will itemise them at the end. If you pick up on any misunderstandings or potential gaps in my knowledge, I would greatly appreciate any input you might have.
The code
The code I have flashed onto my Atmega16 is taken almost line for line from the 'Using the USART in AVR-GCC' tutorial found on this page. All I have added is the #define for F_CPU. The original code did not have a #define for F_CPU so my code would not compile in AtmelStudio 7. Could anyone explain why the author would not have defined F_CPU in their original file? I'm guessing they may have been using some other tool or compiler than Atmel Studio 7 but I can't say for certain.
#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
}
}
The hardware setup
- MCU: Atmega16;
- Toolchain: Atmel Studio 7, flashing with AVR dragon;
- Power supply: 5V rail taken from a university provided development board (which is taken from computer USB). 100nF ceramic disc capacitor used for bypassing on the breadboard power lines
- USB to serial converter: This one. TXD on the USB to serial converter connected to RXD Atmega (Pin 15). RXD on the converter connected to RXD on Atmega (Pin 14).
-
Terminal software: PuTTY (with baudrate of 9600).
Evidence of the incorrect responses
To reiterate, the Atmega should return what was sent to it i.e. OUTPUT should be the exact same as INPUT.
PuTTY output
\begin{array}
\hline
\text{INPUT} & \text{OUTPUT} \\ \hline
\text{f} & \text{&}\\ \hline
\text{f} & \text{6}\\ \hline
\text{z} & \text{>}\\ \hline
\text{d} & \text{0}\\ \hline
\text{space} & \text{0}\\ \hline
\text{x} & \text{8}\\ \hline
\end{array}Oscilloscope Captures
I have used my Picoscope with serial decoding to check that the Atmega is receiving the correct input, which it appears to be. For instance, when I press the 'f' key, it is correctly received. The output is still a '6' (or an ampersand '&' on occasion).
A fix I stumbled upon that I do not understand
If I change the baudrate to 2500in PuTTY, everything is displayed correctly. I chose this value at random and I don't know why it works (it leads me to believe I have made an error somewhere to do with the baudrate but I don't see where given I copied the tutorial almost exactly… I thought).
Questions
- What have I done wrong/what's happening here?
- Why does the original tutorial not #define F_CPU?
- Why does setting the baud rate to 2500 fix the issue? (I suspect this will be answered if question 1 is answered)
Best Answer
I've figured it out! Thanks to the comments about F_CPU in response to the OP I did some investigating (this might be obvious to you all).
Brief solution summary
The Atmega16 was not running at the frequency I thought it was because I did not understand how to change its system frequency. By checking in the fuses in Atmel Studio I could see I was running at 2MHz (this is not the standard clock frequency as far as i'm aware but I won't get into that), and not 7.3728MHz like the tutorial.
F_CPU does not change the clock frequency of the MCU (the Atmega16). The frequency of the Atmega16 was not changed to to 7.3728MHz as was necessary to get the code example to work. It was still running at the frequency defined by the fuses (2MHz in this case, more on this below) so the paper calculation of the desired baudrate differs from what was actually used.
Working code
More detail
Desired baudrate vs what the Atmega was actually doing
The desired baudrate (from the tutorial) was 9600, which is the baudrate I used in PuTTY. The actual baudrate can be calculated using the highlighted equation in Table 60 (page 147) of the Atmega16 datasheet.
In the code example
BAUD_PRESCALE
is UBRR in the calculation.BAUD_PRESCALE
is evaluated as 47 with the values defined forF_CPU
andUSART_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 $$
And this was the root of the issue. The Atmega16 was operating at 2MHz, which meant the value of f_{osc} was different to the tutorial example, which resulted in a baudrate of 2,604 as opposed to 9,600.
Notice that f_osc is the actual system frequency of the MCU, which is not determined by
F_CPU
.So that also answers my 3rd question: changing the baudrate to 2,500 was luckily close enough to the operating baudrate of the MCU that the terminal could correctly interpret the results.
Changing the frequency of the MCU
To change the frequency of the MCU in AtmelStudio 7, go:
The frequency used in the example is not a standard internal clock frequency so I'm going to stick with 2MHz.
Summary of answers to my own questions