How to use printf with UART?

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

I would like to use printf with my UART. Below I have some code that I'm working on.

#include 
#include 

int main(void)
{
	float F_CPU = 1000000;
	float UART_BAUD_RATE = 4800;
	float UART_BAUD_REGISTERS = (((F_CPU / (UART_BAUD_RATE * 16UL))) - 1);

	UCSRB |= (1 << RXEN) | (1 << TXEN);
	UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);

	UBRRL = UART_BAUD_REGISTERS;

	printf("Hello World");

	return 1;
}

When I run this code I nothing happens.

How is printf expected to work? Don't I need to initialize the printf so that it knows to communicate with the UDR.

I can replace the printf line with:

	while ((UCSRA & (1 << UDRE)) == 0) {}; 

	UDR = 'x';	

and the character will print. So I have confidence that the UART is setup to print.

Any help would be appreciated, thanks.

========================

ATmega16
AVR Studio 4.14
STK500
Windows 2000
HyperTerminal

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

Have you looked at the stdiodemo in the winAvr examples folder? printf needs a bit more stuff before it knows where to print. Have a look at that example.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

There is a great, very basic tutorial on reading from serial and printing to serial using printf() on SparkFun. I don't think I'm allowed to post their code here, so here is direct link to tutorial:
http://www.sparkfun.com/commerce...
You might also want to check the "hardware setup" part of the tutorial:
http://www.sparkfun.com/commerce...

They use mega168 - I don't know if it's any different from mega16. You might need to change some registers.

Yesterday I set up my first serial communication using that tutorial and it was painless. Hope it helps you.

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

As printf() is a function in stdio.h I wonder what the documentation for stdio.h in your compiler manual says ;-)

Oh look:

http://www.gnu.org/savannah-chec...

So if you want to know more it looks like your search terms are either fdev_setup_stream or FDEV_SETUP_STREAM (they are different)

The stdiodemo in the compiler examples that John mentioned shows this being used in anger

Cliff

PS I'll move this GCC specific thread to the right forum to appease Bob

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

And I'll add this for anyone who stumbles on this thread in the future: Why assume that stdio should be tied to the UART? Eg why not an LCD display on PORTB?

And when you think a while about these rethorical questions you will reach the conclusion that this can not be known by the toolchain makes. You will have to implement something that sends what is written to stdout to a specific hardware device. (And the documentation tells you what you need to know to do this, and IIRC it has examples on one of the more common devices for stdout, namely a UART).

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

I appreciate the help. The stdiodemo was helpful but it still took me some time to sort this trough. I think this is one of those topics that experience people consider trivial but newbie's (like myself) struggle with because the documentation only makes sense when you learn how to interpret or find it.

Here is the code that I've developed so far:

#include 
#include 
#include 
#include 

#define F_CPU  1000000
#define UART_BAUD_RATE  4800
#define UART_BAUD_REGISTERS  (((F_CPU / (UART_BAUD_RATE * 16UL))) - 1)

int printCHAR(char character, FILE *stream)
{
	while ((UCSRA & (1 << UDRE)) == 0) {};

	UDR = character;

	return 0;
}

FILE uart_str = FDEV_SETUP_STREAM(printCHAR, NULL, _FDEV_SETUP_RW);

int main(void)
{
	UCSRB |= (1 << RXEN) | (1 << TXEN);
	UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);

	UBRRL = UART_BAUD_REGISTERS;

	fprintf(&uart_str, "Hello World\n\r");

	stdout = &uart_str;
	printf("Hello World 2\n\r");

	return 1;
}

The necessary bits of code to setup the printfs is:

FILE uart_str = FDEV_SETUP_STREAM(printCHAR, NULL, _FDEV_SETUP_RW);

printf itself requires this line:

stdout = &uart_str;

I hope that is useful.

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

I've discovered that this won't seem to print a floating point number.

If I insert these lines:

float number = 123.45;
fprintf(&uart_str, "%f", number);

The terminal will only display a question mark (?).

An integer or string works fine by no floating point numbers.

Any ideas?

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

Doesn't you copy of the documentation, linked to above, have a section that starts

Quote:
Since the full implementation of all the mentioned features becomes fairly large, three different flavours of vfprintf() can be selected ...
? If not, please post the link for reference and correction.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:

The terminal will only display a question mark (?).

This is one of those occasions where it's actually preferable to be using a real Makefile (generated by Mfile) rather than the auto-generated Makefiles in AVR Studio. In an Mfile generated Makefile you simply look for this section:

# If this is left blank, then it will use the Standard printf version.
PRINTF_LIB = 
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)

You put a '#' on that first of the three options and take the '#' off the third. The Mfile template also arranges for -lm to be used on the LDFLAGs so you will be linking with the libm.a that is needed for accurate (tight, fast) float support.

If you use Studio it's actually a bit trickier. In the Project Configuration Options go to the library section and make sure both libm.a and libprintf_flt.a are copied from the left to the right pane.

Then in "Custom options" move the highlight down to "[Linker options]" and in the edit box enter "-Wl,-u,vfprintf" then press the [Add] button.

Cliff

PS as Lee says the use of the three different libraries for printf support (and scanf support) IS covered in the user manual. It's kind of unwise to try using a (complex) product like avr-gcc/libc without reading the manual first. I'm always reminded of the German colonel (played by the excellent Gert Frobe) in "Those Magnificent Men in their Flying Machines" trying to operate the plane while reading the manual!

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

Thanks Clawson your advice did the trick. I can successfully print a float to the UART now.

This is what worked in AVR Studio 4:

Project ->
Configuration Options ->
Custom Options ->
[Linker Options] add: "-Wl,-u,vfprintf"

Project ->
Configuration Options ->
Libraries ->
Available Link Objects add: "libm.a" and "libprintf_flt.a"

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

Wha about the efficiency? Isn't it just better to read the registers? Or is it the same thing?

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

rasta, what registers and efficiency do you speak of?

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

You have to read registers. There is no "efficiency" issue about it. There is no other way to receive a character.

 

By the way, that thread is 7 years old. Those posters have not been around for a long time and they won't answer you.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Tue. Mar 29, 2016 - 11:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I mean, how are these two approaches (using printf function with the uartstream or just read and write onto UART Control Registers) are going to be compiled? Is the assembly file generated by one more efficent than the other?
I'd seen that the thread is old but I hoped that someone else would reply to my doubt. Thanks.

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

Like many things in life, it comes down to a compromise. Printf isn't compute or code size efficient, but it does perform a useful task. So you, the programmer, need to decide if the usefulness is worth the cost of code size or execution time. Is you time better spent hand coding an efficient solution or use a widely used generic solution? In days gone by, code size was a critical factor - you might only be able to buy a part with 4k of eprom or flash. Nowadays, we don't have those limitations. You might compare it to choosing between riding a pushbike or driving the car. 

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

Rasta Penguiman wrote:

I mean, how are these two approaches (using printf function with the uartstream or just read and write onto UART Control Registers) are going to be compiled? Is the assembly file generated by one more efficent than the other?
I'd seen that the thread is old but I hoped that someone else would reply to my doubt. Thanks.

Define your term "efficient".

 

Anyway the redirection through a stream is obviously going to add an "extra layer". If I write:

void uart_init(void) {
    UCSRB= (1 << TXEN);
    UBRR = 37;
}
void uart_putchar(char c) {
    wait_UDRE();
    UDR = c;
}

void uart_puts(char * str) {
    while (*str) {
        uart_putchar(*str++);
    }
}

int main(void) {
    uart_init();
    uart_puts("hello");
}

Then the path to sending characters here is obvious. A pointer to the string "hello" is passed to uart_puts() and it dereferences that pointer and passes each character in turn (until the final 0x00) to the uart_putchar(). It's all very simple.

 

In the FDEV case I would use something like:

void uart_init(void) {
    UCSRB= (1 << TXEN);
    UBRR = 37;
}
void uart_putchar(char c, FILE * stream) {
    wait_UDRE();
    UDR = c;
    return 0;
}

FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

int main(void) {
    uart_init();
    stdout = &uart_str;
    puts("hello");
}

The "hidden bit" here is the system function puts(). It's not that hidden because you can read the source code here:

 

http://svn.savannah.nongnu.org/v...

 

So the code is basically this:

ATTRIBUTE_CLIB_SECTION
int
puts(const char *str)
{
	char c;
	int rv = 0;

	if ((stdout->flags & __SWR) == 0)
		return EOF;

	while ((c = *str++) != '\0')
		if (stdout->put(c, stdout) != 0)
			rv = EOF;
	if (stdout->put('\n', stdout) != 0)
		rv = EOF;

	return rv;
}

which actually looks remarkably familiar - this is almost the same as the uart_puts() I wrote above, except that it handles the possibility of the putchar() function returning non 0 (except in my implementation this will never happen as the characters are always reported as sent OK). It also sends a final '\n' at the end. But the key thing is that it's simply calling:

stdout->put(c, stdout)

to send each character. Whereas previously I was using:

uart_putchar(*str++);

so, yes, there is a level of indirection because, instead of just a hard coded CALL to uart_putchar, it is using the "put" member of the FILE structure (that was previously set to point to uart_putchar in the FDEV_SETUP) and will be calling it indirectly using an ICALL.

 

But other than that these two are almost as "efficient" (whatever that means!).

 

Of course the advantage of using the stream is that you can now write "normal" C programs that use normal <stdio> functions like puts(), putchar(), printf(). You could even write this on a PC:

int main(void) {
    puts("hello");
    printf("%d / %u = %f", 22, 7, (double)22/7);
    putchar('\n');
}

then just take this to the AVR and add:

int main(void) {
    uart_init();
    stdout = &uart_str;
    puts("hello");
    printf("%d / %u = %f", 22, 7, (double)22/7);
    putchar('\n');
}

and have an expectation that it would work in the same way and produce the same result. The advantage of this is that it only takes about 30 seconds to try:

~$ cat test4.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void) {
    puts("hello");
    printf("%d / %u = %f", 22, 7, (double)22/7);
    putchar('\n');
}

~$ gcc -Os test4.c -o test4
~$ ./test4
hello
22 / 7 = 3.142857
~$

so I know what it is going to look like on a PC terminal after I build this for AVR, program it in, connect up the UART and run a terminal program (only I didn't have to do any of that to see what the output might look like!).

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

back in 2009, js wrote:
Have you looked at the stdiodemo in the winAvr examples folder?

https://www.avrfreaks.net/comment... updates the reference for Atmel Studio 7 ...

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And I'll add this for anyone who stumbles on this thread in the future: Why assume that stdio should be tied to the UART? Eg why not an LCD display on PORTB?

a small example of using an LCD display? (a 16x2 standard display that is commonly used)

if it's easy...

if I'm wrong, correct this and do not criticize :o)

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

Did you look at the example in the manualhttps://www.gnu.org/savannah-checkouts/non-gnu/avr-libc/user-manual/group__avr__stdio.html (linked earlier in this thread)

 

It really is just a matter of providing a function to send a single character to the output device - whatever that device may be.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
It really is just a matter of providing a function to send a single character to the output device - whatever that device may be.

 

it's all, of course, everything "and so it is clear" ... virtually, when in the head and thoughts you imagine ...

a piece of code, as an example, will not be a hindrance for a better understanding

if I'm wrong, correct this and do not criticize :o)

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

sunjob wrote:
it's all, of course, everything "and so it is clear" ... virtually, when in the head and thoughts you imagine ... a piece of code, as an example, will not be a hindrance for a better understanding

 

I think my head exploded when reading that......... Even Yoda was more concise.

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

El Tangas wrote:
See if this helps:
Shame all the #include in that have been "eaten" !

 

It took a bit of searching to find this but the original thread was here:

 

http://legacy.avrfreaks.net/inde...

 

However I don't see what is wrong with:

awneil wrote:
Did you look at the example in the manual: https://www.gnu.org/savannah-che... (linked earlier in this thread)

That example has complete code for LCD including a full HD44780 driver. In stdiodemo.c it shows you the key line:

FILE lcd_str = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);

Also...

  stdout = stdin = &uart_str;
  stderr = &lcd_str;

They've chosen in the example to assign the LCD strream to stderr but for normal printf() use you would ignore their UART code and assign ;cd_str to stdout. The lcd.h/lcd.c implements:

int	lcd_putchar(char c, FILE *stream);

that is used in the FDEV setup. The implementation is:

int
lcd_putchar(char c, FILE *unused)
{
  static bool nl_seen;

  if (nl_seen && c != '\n')
    {
      /*
       * First character after newline, clear display and home cursor.
       */
      hd44780_wait_ready(false);
      hd44780_outcmd(HD44780_CLR);
      hd44780_wait_ready(false);
      hd44780_outcmd(HD44780_HOME);
      hd44780_wait_ready(true);
      hd44780_outcmd(HD44780_DDADDR(0));

      nl_seen = false;
    }
  if (c == '\n')
    {
      nl_seen = true;
    }
  else
    {
      hd44780_wait_ready(false);
      hd44780_outdata(c);
    }

  return 0;
}

As you can see this then calls down to the hd44780.h/.c stuff. My code in the post that El Tangas linked to:

/*
 * JimsCstarter.c
 *
 * Created: 10/27/2013 10:34:31 PM
 *  Author: jgmDESIGNS
 */

#define F_CPU 16000000ul
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include <math.h>
#include "lcd.h"

int   outlcd(char c, FILE *stream);
FILE mystdout = FDEV_SETUP_STREAM(outlcd, NULL, _FDEV_SETUP_WRITE);

void lcdini(void)
//init LCD
{
   lcd_init(LCD_DISP_ON_CURSOR_BLINK);
   stdout = &mystdout;
}

int   outlcd(char c, FILE *stream) {
   lcd_putc(c);
   return 0;
}

int main(void)
{
   lcdini();
   printf("Hello World!!");

    while(1)
    {
        //TODO:: Just sit here forever
    }
} 

simply assumes the existence of an lcd_init() and an lcd_putc() beneath all this (in my case I assume Fleury).

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

thanks, I will read and understand :)

if I'm wrong, correct this and do not criticize :o)