8
\$\begingroup\$

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

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

Scope capture on the RX pin of the Atmega16 showing that the correct character is being sent via the terminal software ('f')

Scope capture on the TX pin of the Atmega16 showing that an undesired response is being sent back ('6')

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

  1. What have I done wrong/what's happening here?
  2. Why does the original tutorial not #define F_CPU?
  3. Why does setting the baud rate to 2500 fix the issue? (I suspect this will be answered if question 1 is answered)
\$\endgroup\$
  • 2
    \$\begingroup\$ Simply defining F_CPU to some value doesn't make the micro run at that frequency. F_CPU should be defined as the frequency at which you have configure the micro to run - but I don't see any evidence that you've configured this anywhere... \$\endgroup\$ – brhans Apr 25 '18 at 13:20
  • \$\begingroup\$ Well written question. The only thing that would improve it would be a schematic. \$\endgroup\$ – Blair Fonville Apr 25 '18 at 13:20
  • \$\begingroup\$ +1 just for the \$\LaTeX\$ table. \$\endgroup\$ – Arsenal Apr 25 '18 at 13:35
  • \$\begingroup\$ I notice that you have no external crystal on your breadboard. Are you using the internal RC clock? What frequency do you expect the processor to be running at? \$\endgroup\$ – scotty3785 Apr 25 '18 at 16:22
  • \$\begingroup\$ Thanks to your discussion about F_CPU I did some investigating and playing around and posted the solution. I imagine it's obvious to you (as it is to me now) but it might help someone else. \$\endgroup\$ – daviegravee Apr 28 '18 at 3:31
0
\$\begingroup\$

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

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

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.

Table of equations to calculate baudrate and UBRR from page 147 Atmega16 datasheet

In the code example BAUD_PRESCALE is UBRR in the calculation. BAUD_PRESCALE is evaluated as 47 with the values defined for F_CPU and 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 $$

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:

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

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

  1. What have I done wrong/what's happening here? Answer: Didn't actually change the clock frequency to the clock frequency in the tutorial which resulted in a different baudrate to what was expected which put the terminal software (PuTTY) out of sync with the MCU
  2. Why does the original tutorial not #define F_CPU? Answer: Still not entirely sure but my guess would be it is defined in a makefile not given in the tutorial and that the author was not making use of an IDE like Atmel Studio
  3. Why does setting the baud rate to 2500 fix the issue? (I suspect this will be answered if question 1 is answered) Answer: Luckily guessed a number close to the baudrate of the Atmega16
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.