Using PRINTF on an LCD

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

Trying to use the PRINTF statement to output Hello World! on an LCD. I am using Peter Fleury's library and am trying to connect LCDPUTC to the STDOUT.

Here is what I have:

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

#define F_CPU 16000000ul
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "lcd.h"

void lcdini(void)
//init LCD
{
	lcd_init(LCD_DISP_ON_CURSOR_BLINK);

}
void initstdio(void)
{int   outlcd(lcd_putc(char c), FILE *stream);
	FILE mystdout = FDEV_SETUP_STREAM(outlcd, NULL, _FDEV_SETUP_WRITE);
	stdout = &mystdout;
	
}
int main(void)
{
lcdini();
	printf("Hello World!!"); 

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

And the errors I get:

Quote:
Error 1 expected declaration specifiers or '...' before 'lcd_putc' Z:\AVR Test Programs\Jims_C_Adventure\JimsCstarter\JimsCstarter\JimsCstarter.c 25 15 JimsCstarter

And:
Quote:
Error 2 'outlcd' undeclared (first use in this function) Z:\AVR Test Programs\Jims_C_Adventure\JimsCstarter\JimsCstarter\JimsCstarter.c 26 18 JimsCstarter

THis part:

void initstdio(void)
{int   outlcd(lcd_putc(char c), FILE *stream);
	FILE mystdout = FDEV_SETUP_STREAM(outlcd, NULL, _FDEV_SETUP_WRITE);
	stdout = &mystdout;
	
}

I took from a UART experiment that was working and I changed a few things - Hence why it no longer works... :?

Can someone point to where I am going wrong...

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

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"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, RSLogix user

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

Not a direct answer to your question, but I always just use sprint followed by a call to the lcd output routine with the desired X and Y.

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

I have this setup for a system where I'm using both the USART (with printf) and LCD (with fprintf)

	FILE com0_str = FDEV_SETUP_STREAM(usart0_putchar, usart0_getchar, _FDEV_SETUP_RW);
	FILE lcd_str = FDEV_SETUP_STREAM (lcd_putchar, NULL, _FDEV_SETUP_WRITE);
.
.
printf_P (PSTR ("\n\rAuto mode\n\r"));
.
.
fprintf_P (lcd, PSTR ("Auto"));

I added this to the standard LCD driver which came with stdiodemo with winAvr

#define lcd (&lcd_str)
extern FILE lcd_str;

edit thread here https://www.avrfreaks.net/index.p... but I forgot to do anything about the last post I made. :(

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Jim,

although in your code you never actually call initstdio(), it wouldn't work because your FILE mystdout would be a local variable with a lifetime within initstdio() only. Try to change the statements within initsdtio() into mainline code.

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

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

Jim,

Try this. I have Fleury-LCD installed so I just set up an include path so lcd.h could be found and I used Add-existing-Add_as_link to add lcd.c to the project.

It builds without error or warning but as I don't have an LCD wired up just now I cannot actually test it.

The key thing here is that Fleury's lcd_putc() does not have the function interface that FDEV_SETUP_STREAM() requires so you need to implement the outlcd() function to translate from one to the other. I think you knew this from what you had attempted but your:

int   outlcd(lcd_putc(char c), FILE *stream); 

is not sufficient to map one function to the other as it's only a declaration, you need to instantiate outlcd() so the compiler actually generated code to translate from the two parameter interface to the single parameter lcd_putc().

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

#define F_CPU 16000000ul
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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
    }
} 

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

THanks all. I seem to be on the same idea as John is after I powered down and went to bed... I recalled John having some issue, and could not remember what he did. What I have to find in Johns quests is the thread where he had four usarts and needed to create four streams.

When I woke this morning I went here:

http://www.nongnu.org/avr-libc/u...
And found the part where the two streams explained.
Now
@Cliff,
I knew I did not have the thing formatted correctly, but I gave up out of being tired and I had to get the four year old ready this morning to keep banging my head against the screen. I will try your example after I get the little guy off to school this morning.

Oh FWIW, in my searches last night I came across this:
http://efundies.com/avr/avr_prin...

Quite a good read and this statement:

Quote:
The Problem With printf()

printf() is an excellent function that adds lots of utility to your programs. The big problem with it is that it is pretty large. When you use printf() in your program, your code size automatically jumps by about 1,444 bytes to your code. This is a one time penalty, so if you are going to use printf() once, you might as well use it as often as you want.
Streams and stdio.h

Explained why my code tripled in size in another experiment. One thing I will have to look at is if this 1.5k increase is truly a one time thing as the text states or does it increase by that much for each stream. But that's for another experiment.

I'll get on this this morning and report back. Thanks all

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

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"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, RSLogix user

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

This seems to be a gcc specific solution for a problem that doesn't exist in the imagecraft compiler. printf() calls putchar(), which calls a putcuart() and putclcd() function that select which of these to call with a global. I call it fd because that makes it look all computer sciency like a "file descriptor", but its just 0 for uart and 1 for lcd etc. Perhaps this simple solution would work in the free compiler also?

Imagecraft compiler user

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

Quote:

One thing I will have to look at is if this 1.5k increase is truly a one time thing as the text states or does it increase by that much for each stream. But that's for another experiment.

The "costs" for printf (in avr-gcc 4.7.2/AVR-LibC 1.8.0) are:

vfprintf in libc.a:           1476 byets
vfprintf in libprintf_min.a:  1194 bytes
vfprintf in libprintf_flt.a:  2978 bytes

However many times you call (s)printf() in your code it only links a single copy of vfprintf() into the flash image so you only pay these costs once though each invocation of (s)printf() does cost a few more flash bytes than a simple CALL simply because it usually has some quite complicated parameter setup to make the call each time. (I just added a second call to sprintf to a test program I was using and it cost 36 bytes to make the call).

Quote:
Perhaps this simple solution would work in the free compiler also?
Sure you could do it that way too. But if you have stdout and stderr why not just use them. It also gives access to all the f() output functions that allow you to nominate a stream for output like:

fputc('A', lcd_stream);
fprintf(uart_stream, "Hello world");

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

Quote:
it wouldn't work because your FILE mystdout would be a local variable with a lifetime within initstdio() only.
Not only that, but you declared a function inside another function.

Regards,
Steve A.

The Board helps those that help themselves.

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

Hmm, Cliff's code worked(well of course it did), but one thing has me a little puzzled.

In this part:

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

What is this doing?:

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

I was assuming that the first line:

int   outlcd(char c, FILE *stream);

Took care of everything.

Trying to get a grasp on that.

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

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"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, RSLogix user

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

Quote:

I was assuming that the first line:


Never ass-u-me anything ;-)

In C you can declare and you can define both variables and functions. With functions it's easy to spot a declaration from a definition as one has the interface then simply a semi-colon and the other has the interface and then the actual code of the function between { and }.

You don't actually have to declare functions. If I write:

void write_portB(uint8_t n) {
  PORTB = n;
}

int main(void) {
  write_portB(0xA5);
}

that just works "as is" because, by the time the compiler comes to the invocation of write_portB() in main() it already knows it's a function that returns nothing (void) and takes one uint8_t as input. But suppose I had written things in this order:

int main(void) {
  write_portB(0xA5);
}

void write_portB(uint8_t n) {
  PORTB = n;
}

Now, when the compiler is compiling main() it comes to a call to write_portB() but it doesn't know if the value in the brackets should be passed as a uint8_t or an int16_t or a float or a char or what. So I have to tell it "up front" by including a declaration of the function before it appears:

void write_portB(uint8_t n);

int main(void) {
  write_portB(0xA5);
}

void write_portB(uint8_t n) {
  PORTB = n;
}

Now the compiler knows all about write_portB() at the point main() calls it. This declaration would be even more important if write_portB() lived in another file (in which case I would likely have put the declaration in a .h file and #include#d that in the file with main()).

So all I did in my code above was to declare the interface to outlcd() early because FDEV_SETUP_STREAM() needed to know that it had a "int fn(char, FILE *)" interface. In fact in a declaration you don't even need to include variable names, just types so in the example in this post I could have used:

void write_portB(uint8_t);

int main(void) {
  write_portB(0xA5);
}

void write_portB(uint8_t n) {
  PORTB = n;
}

without naming 'n' in the declaration. But often the names help. Something like:

lcd_string(int x, int y, char * str);

tells you more about the parameters (for the benefit of the reader/maintainer) than:

lcd_string(int, int, char *);

where it's perhaps not so clear what the two int's are there for.

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

I'll have to read this and the example on the nongnu site and get a handle on it. Thanks

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

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"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, RSLogix user

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

I am not an expert, I see it from my user point of view like this:

FILE mystdout = FDEV_SETUP_STREAM(put, get, mode); 

In this initialization note the arguments put, get, mode.

You want to use it for LCD.
So for put you have to provide a function for writing a char to Lcd.

For get you have to provide a function for reading a char from Lcd. Here you do not want to read from Lcd, so you write NULL.

Mode can be read, write or read/write. You need only write so the mode is _FDEV_SETUP_WRITE.

Quote:
I was assuming that the first line:
Code:
int outlcd(char c, FILE *stream);
Took care of everything.
But this is only function prototype. Note that there is no code there.
You have to provide the complete function definition.

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

Ok, after reading the STDIODEMO program and the associated files Cliffs extra line IS there, just in a different file.

OK, now that I have that done I just need to understand better what exactly is going on, and why this:

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

Is just sitting where it is between the LCD_Ini function and MAIN

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

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"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, RSLogix user

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

The other code expects a pointer to a function that takes both a char and a FILE *
Give it anything else and the compiler will barf. As such, we have no need for the FILE * so we just pass on the char. Call it an adapter plate.

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

Quote:
and why this:
Code:
int outlcd(char c, FILE *stream) {
lcd_putc(c);
return 0;
}
Is just sitting where it is between the LCD_Ini function and MAIN

Look at a bad order of declarations:

void lcdini(void)
//init LCD
{
   lcd_init(LCD_DISP_ON_CURSOR_BLINK);
   stdout = &mystdout; // error: mystdout not declared yet 
}

FILE mystdout = FDEV_SETUP_STREAM(outlcd, NULL, _FDEV_SETUP_WRITE); // error: outlcd undeclared

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

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

All things used in declarations/definitions must have been declared before.

Right order:

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

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 main(void)
{
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Is just sitting where it is between the LCD_Ini function and MAIN

As I said above:
Quote:
The key thing here is that Fleury's lcd_putc() does not have the function interface that FDEV_SETUP_STREAM() requires so you need to implement the outlcd() function to translate from one to the other.

The FDEV_SETUP_STREAM() stuff requires just one function for outputting but rather than the obvious:

void sendbyte(char c);

it asks that the interface be:

int sendbyte(char c, FILE * stream);

Yet anyone who's ever written an lcd_putchar() or uart_putchar() (including Mr Fleury) has very likely made the interface:

void lcd_putchar(char c);

It only takes a single char as input and it has no return. While the FDEV stuff is looking for a function that returns an int (so it can return 0 to say "yup, that worked OK" or non-0 to say "there was a problem"). So to be able to use that function you therefore need to "wrap it" in a function that to the outside world gives the interface that FDEV expects but that at the core it calls the byte output function as Mr Fleury or whoever wrote it.

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

On the outside this function implements the interface that FDEV is looking for. When FDEV (stdio) calls it then it will pass both a character to transmit and a pointer to an output stream. However for the simple case we're not actually interested in using that. So inside the function I just pass on the 'c' that has been sent in the way that Mr Fleury likes to see it. Finally, the FDEV stuff is waiting for us to return a 0 int to say "worked OK" to I do just that with the "return 0;" statement.

IOW this routine is simply an "interface translator". Such functions are often called "shims" in systems programming.

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

I understand what you are saying Cliff, but I am still stuck I the comprehension. Not your fault. I will just have to sit and read further on this with the textbooks I have.

I like the analogy Kartman makes about the adapter plate as well

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

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"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, RSLogix user