PC receives only NULL characters from MCU using USART

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm currently trying to send just a few numbers from my atmega328P to PC using USART, through a USB adapter (PL2303HX).

 

My MCU program simply initializes USART and sends some numbers in a loop:

 

    //I haven't set any fuse bits
    #define F_CPU 1000000

    #include <avr/io.h>
    #include <util/delay.h>

    //TXD pin is the pin connected to my adapter's RXD pin
    #define TXD_DDR DDRD
    #define TXD_PORT PORTD
    #define TXD_PIN PORTD1

    //lower values (600 or less) and higher (above 9600) don't work at all
    #define BITS_PER_SECOND 1200

    #define BAUD_RATE() (((uint16_t)((uint16_t)F_CPU / (16 * ((uint16_t)BITS_PER_SECOND))) - 1))

    void initializeUSART()
    {
        UBRR0L = BAUD_RATE();
        UBRR0H = BAUD_RATE() >> 8;

        //turn on transmission
        UCSR0B = 1 << TXEN0;

        TXD_DDR = 1 << TXD_PIN;
        TXD_PORT = 1 << TXD_PIN;

        //set 8-bit character size, 1 stop bit, synchronous mode, no parity bits
        UCSR0C = (1 << UCSZ00) | (1 << UCSZ01) | (1 << UMSEL00) | (1 << UMSEL01);
    }

    void send(uint8_t number)
    {
        //wait until UDR is empty
        while(!(UCSR0A & (1 << UDRE0)));

        UDR0 = number;
    }

    int main()
    {
        initializeUSART();

        while(1)
            for(uint8_t i = 0; i < 100; i++)
            {
                send(i);

                _delay_ms(500);
            }
    }

   

The PC program just keeps calling ReadFile and prints anything it returned:
   

while(1)
    {
        uint8_t buffer[4096] = {};

        DWORD readBytesCount;

        //portHandle is a handle to the USB adapter's COM port
        if(!ReadFile(portHandle, buffer, sizeof(buffer), &readBytesCount, NULL))
            printf("ReadFile failed with code %d\n", GetLastError());
        else if(readBytesCount > 0)
        {
            printf("Read %d bytes: ", readBytesCount);

            for(DWORD i = 0; i < readBytesCount; i++)
                printf("%d ", buffer[i]);

            printf("\n");
        }
    }

The adapter has a few diodes indicating data transmission and the diodes glow properly, once every 0.5 second.

 

 

The problem is the data sent to my PC are only zeros, regardless of what I actually transmissed.

 

Moreover, when I increase the delay to 2000 ms, I get 1-4 zeros each 2 seconds.

 

Screenshot from Realterm:

Realterm output

As you can see, it keeps reporting "UART receiver framing error". I found a few posts about this error, but none of them helped me.

 

Have you any idea what causes the problem?

This topic has a solution.
Last Edited: Sat. Jun 15, 2019 - 12:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You probably have the AVR running on the internal RC oscillator and do not have the baud rate set up properly.

I would look in the data sheet for the actual number that goes into the baud rate register for your CPU clock and enter that in directly and see if it works as your formula looks wrong.

Jim

Edit: I think your CPU frequency of 1Mhz is wrong. I don't think the Mega32 has a DIV8 fuse and runs at 8Mhz natively....I might be wrong on that though

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

Last Edited: Sat. Jun 15, 2019 - 11:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your reply. I accidentally typed 'atmega32' instead of 'atmega328P', sorry about that. What's wrong with my formula?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're casting FCPU to a uint16. 1 million doesn't fit into 16 bits - you need at least 20.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The mega32 runs on 1MHz RC by default.
Your BAUD_RATE() macro is rubbish. Where did you get it from?
Either use a sensible one or look up the UBBR values from the datasheet table.
Or remove the casts from your silly macro. They will fail with any uint32_t expressions.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a header file provided by avr-libc, might be worth reading up on

 

util/setbaud.h

 

Failing that, a direct replacement for your macro (taken from setbaud.h) would be

#define UBRR_VALUE (((F_CPU) + 8UL * (BAUD)) / (16UL * (BAUD)) -1UL)

where BAUD is your BITS_PER_SECOND.

The +8UL * BAUD is for rounding, so it will round up/down to the closest value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your reply. I have no idea why I didn't notice such a simple bug. I've already checked a lot of examples from the Internet and almost each one had a different macro. I changed it to:

#define BAUD_RATE() ((((uint32_t)((uint32_t)F_CPU / (2 * ((uint32_t)BITS_PER_SECOND))) - 1)))

Realterm still keeps outputting only zeros, whereas my program returns:

 

96, 2, 65, 3, 112, 4, 66, 5, etc.

 

As you can see, every other value is the next i value sent from the for loop. How can I get rid of the junk between?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your reply. After replacing my macro with UBRR_VALUE, my program returns only 127. Realtime outputs only 224 and 0 with BAUD set to 1200 and random numbers when it's set to 9600:

12 18 2 28 14 30 224 96 16 98 12 108 18 110 130 112 28 114 14 124 30 126 240 128
 96 130 16 140 98 142 140 144 108 146 18 156 110 158 226 224 112 226 28 236 114 
238 142 240 124 242 30 252 126 254 252 0 254 128 2 254 96 12 254 130 14 254 144 
16 254 140 18 254 98 28 254 142 30 254 236 96 254 144 98 254 108 108 254 146 110
 254 146 112 254 156 114 254 110 124 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 //set 8-bit character size, 1 stop bit, synchronous mode, no parity bits
        UCSR0C = (1 << UCSZ00) | (1 << UCSZ01) | (1 << UMSEL00) | (1 << UMSEL01);

You want async mode, not sync mode! 

 

#define BAUD_RATE() ((((uint32_t)((uint32_t)F_CPU / (2 * ((uint32_t)BITS_PER_SECOND))) - 1)))

Still looks wrong - where did you get 2 from?? Read the datasheet to get the lowdown.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Isn't asynchronous mode based on interrupts? If it is, I prefer synchronous mode, as I'm not going to send a lot of data (and I want to get the synchronous mode to work first). The macro I posted is taken from the datasheet for synchronous mode, page 146.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would also recommend looking at the baud rate tables in the datasheet, section 19.11 "Examples of baud rate setting".

This will show you the systenatic error in the timing for different baud rates for various fclk values.

If you're uisng 1MHz clock, you will notice that 9600 baud gives a 7% systematic error if you have U2Xn = 0.

Thta's not going to work, unless your internal oscillator happens also to be about 7% off in the right direction.

 

Bottom line, use 'double speed' mode ie. U2Xn = 1.

That gives a 0.2% systematic error for 9600 baud, which will work fine, assuming your internal oscillator is within a few percent of 1MHz which it probably will be.

You then need to change your macro to divide by 8 instead of by 16.

Or just read the UBBR value fron the table and use that.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

George299 wrote:
Isn't asynchronous mode based on interrupts? If it is, I prefer synchronous mode, as I'm not going to send a lot of data (and I want to get the synchronous mode to work first). The macro I posted is taken from the datasheet for synchronous mode, page 146.

 

Err no. Standard serial data is asynchronous as in the clocking between sender and receiver is not the same. Interrupts or no interrupts. So you want async because that's the way it's been for over 60 years. You misunderstanding of synchronous in this context explains the wrong macro. The division is 8 or 16 depending on the state of the U2X bit.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you all so much! It finally works after clearing the UMSEL00 bit (for asynchronous operation), setting BITS_PER_SECOND to 1200 and the BAUD_RATE macro to:

#define BAUD_RATE() ((((uint32_t)((uint32_t)F_CPU / (16 * ((uint32_t)BITS_PER_SECOND))) - 1)))

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good to hear. If you want some background on async serial - here's the wikipedia blurb on it:

https://en.wikipedia.org/wiki/As...

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Make sure you are NOT accidentally using the internal RC oscillator as your USART source.  You can very easily create mysterious problems that you will chase down for days or weeks, that won't be solved & wouldn't appear in the first place, if you use a good 30 cent clock source.  It is far far easier to investigate other problems when 100% know that your clock source is not the potential issue.    If you must use the internal clock, you must first measure its freq (toogle an io pin & use a freq counter (best), or digital scope).  In a pinch you can look at the divided output pulses on the TX line.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sat. Jun 15, 2019 - 03:20 PM