printf() & co.

Go To Last Post
67 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!
I'm wondering if is true that with MPLAB one can use the appropriate printf() function in a PIC. Is it true? In this case with an AVR inside Atmel Studio exist too, without necessity to do you own function? It seems to exist but it doesn't work, at least with an atmega328...

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

No idea about MPLAB but printf works very well on all chips with UART functionality.

You have to tell stdio where to send stuff. See attached example from winAvr.

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

What does innappropriate mean
What does 'it does not work' mean? (many things do not work)

Choosing the apprpriate format may be very inappropriate if one wants to display conversions at least :
ex, on a PC

$ cat a.c
#include 
int main() {
        int8_t c='U';
        printf("%c %x %d\n",c,c,c);
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The user manual:

http://www.nongnu.org/avr-libc/u...

The example in user manual (for which John also posted the files above):

http://www.nongnu.org/avr-libc/u...

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

I have some troubles to implement this. I mean, there a FILE pointer initialization which i don't understand how to implement in a USART function.

What I wrote is that:

void USARTWriteString(int n, char* data)
{
	unsigned int cyc=0;
	
	for (cyc=0; cyc

and

void USARTReadString(int n, char* stringa)  
{
	int i;
	for (i=0; i

and I initialize with:

void USARTInit(uint16_t baud)
{

   //Set Baud rate
   UBRR0 = 103; //= F_CPU/(16*baud) - 1;

   /*Set Frame Format
   >> Asynchronous mode
   >> No Parity
   >> 1 StopBit
   >> char size 8 bit
   */

   UCSR0C|=(1<<UCSZ00)|(1<<UCSZ01);

   //Enable The receiver and transmitter

   UCSR0B|=(1<<RXEN0)|(1<<TXEN0);

}

And the character managing, like this:

char USARTReadChar()
{
	//Wait until a data is available

	while(!(UCSR0A & (1<<RXC0)))
	{
		//Do nothing; you can BLINK a LED here :P
	}

	//Now USART has got data from host
	//and is available in buffer

	return UDR0;
}


void USARTWriteChar(char data)
{
	//Wait untill the transmitter is ready

	while(!(UCSR0A & (1<<UDRE0)))
	{
		//Do nothing
	}

	//Now write the data to USART buffer
	UDR0=data;
}

All is wrote with the data sheet help. But There's a lot of software abstraction in your guide that I cant orientate myself. Can I have a little more help? :)

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

But the whole point of using printf is so that you don't need to write the string routines, you only need a getchar and a putchar routine. Did you not at all look at the pages linked to above?

Regards,
Steve A.

The Board helps those that help themselves.

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

Just checking but did you read this example in the user manual:

http://www.nongnu.org/avr-libc/u...

Ignore the LCD stuff and concentrate on UART alone. Therefore it suggests you need one character input function and one character output function. You don't need to write character string functions because you get all that "for free" when you wire up to stdio. You then get printf, scanf, gets, puts and so on. So as the example shows the prototype for the character output function must be:

int uart_putchar(char c, FILE *stream);

and the input function you provide must have the interface:

int uart_getchar(FILE *stream);

So forget your USARTWriteString(), forget your USARTReadString(). You still need your USARTInit() and you must modify your single character send/receive routines as follows:

int USARTReadChar(FILE *stream) 
   //Wait until a data is available

   while(!(UCSR0A & (1<<RXC0)))
   {
      //Do nothing; you can BLINK a LED here :P
   }

   if (UCSR0A & _BV(FE0))
	  return _FDEV_EOF;
   if (UCSR0A & _BV(DOR0))
	  return _FDEV_ERR;
   //Now USART has got data from host
   //and is available in buffer

   return (int)UDR0;
} 

void USARTWriteChar(char data, FILE *stream)
{
   if (data == '\n') {
     USARTWriteChar('\r', stream)
   }

   //Wait untill the transmitter is ready

   while(!(UCSR0A & (1<<UDRE0)))
   {
      //Do nothing
   }

   //Now write the data to USART buffer
   UDR0=data;
} 

Finally you are in a position to write main():

#include 

int USARTReadChar(FILE *stream);
void USARTWriteChar(char data, FILE *stream);

FILE uart_str = FDEV_SETUP_STREAM(USARTWriteChar, USARTReadChar, _FDEV_SETUP_RW);

char buff[20];

int main(void) {
  USARTInit(9600);
  stdout = stdin = &uart_str;
  printf("My God, it works\n");
  while(1) {
    gets(buff);
    puts(buff);
  }
}

fgets() may be "safer" than gets() (which reads stdin by implication) as it lets you specify the buffer size:

  while(1) {
    fgets(buff, 20, stdin);
    puts(buff);
  }

The '\n' handling is different though.

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

clawson wrote:
Just checking but did you read this example in the user manual:

http://www.nongnu.org/avr-libc/u...

Ignore the LCD stuff and concentrate on UART alone. Therefore it suggests you need one character input function and one character output function. You don't need to write character string functions because you get all that "for free" when you wire up to stdio. You then get printf, scanf, gets, puts and so on. So as the example shows the prototype for the character output function must be:

int uart_putchar(char c, FILE *stream);

and the input function you provide must have the interface:

int uart_getchar(FILE *stream);

So forget your USARTWriteString(), forget your USARTReadString(). You still need your USARTInit() and you must modify your single character send/receive routines as follows:

int USARTReadChar(FILE *stream) 
   //Wait until a data is available

   while(!(UCSR0A & (1<<RXC0)))
   {
      //Do nothing; you can BLINK a LED here :P
   }

   if (UCSR0A & _BV(FE0))
	  return _FDEV_EOF;
   if (UCSR0A & _BV(DOR0))
	  return _FDEV_ERR;
   //Now USART has got data from host
   //and is available in buffer

   return (int)UDR0;
} 

void USARTWriteChar(char data, FILE *stream)
{
   if (data == '\n') {
     USARTWriteChar('\r', stream)
   }

   //Wait untill the transmitter is ready

   while(!(UCSR0A & (1<<UDRE0)))
   {
      //Do nothing
   }

   //Now write the data to USART buffer
   UDR0=data;
} 

Finally you are in a position to write main():

#include 

int USARTReadChar(FILE *stream);
void USARTWriteChar(char data, FILE *stream);

FILE uart_str = FDEV_SETUP_STREAM(USARTWriteChar, USARTReadChar, _FDEV_SETUP_RW);

char buff[20];

int main(void) {
  USARTInit(9600);
  stdout = stdin = &uart_str;
  printf("My God, it works\n");
  while(1) {
    gets(buff);
    puts(buff);
  }
}

fgets() may be "safer" than gets() (which reads stdin by implication) as it lets you specify the buffer size:

  while(1) {
    fgets(buff, 20, stdin);
    puts(buff);
  }

The '\n' handling is different though.

It works. :D
Your example with the gets part lead me to understand well the behaviour.
I was missing the uart_str initialization mechanism. Thanks to you and google for other examples for understanding the stream inizialization, now it works. ;) Thanks!

Last Edited: Fri. Jul 19, 2013 - 05:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And with that I'll move this thread to GCC Forum as it's only relevant to the avr-gcc compiler.

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

I am wondering if it's possible to implement the floating support of the printf. I know that I neet to add an option like "-lprintf_flt" but since i'm using AVR Studio 6 and I'm not real expert in the exact toolchain mechanism, I dont where to go to tell to implement the floating conversion.

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

If you look to the Solution Explorer and right click libraries as shown in the first picture below you should reach the dialog in the second picture. Make sure the printf_flt option is selected. Then in the Project Properties under AVR/GNU Linker look for "General" and in that make sure the last option (that mentions vprintf is ticked) as shown in the 3rd picture.

Attachment(s): 

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

So 17 days ago we gave you a WORKING example of printf and you still can't get it to work? :roll:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

What if I ask to Clawson if it is normal that I dont have the "Libraries" menu? I tried to find something in menus, in vain. I mean, searching on google and here there's a lot about my problem, but how can I modify my compiler option adding the fprintf_flt option if do not find the graphical path to follow? I've Atmel Studio 6, I suppose that Clawson is using the 5 or another version with a difference on the gui.. (?)

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

OP wrote:
if is true that with MPLAB one can use the appropriate printf() function in a PIC. Is it true?

Of course. As mentioned, similar with gcc for AVRs.
I have used XC8 and SDCC PIC toolchains under MPLABX and printf() works seamlessly on both. What is more, if you run the MPLABX PIC simulator, then output of printf() is redirected to an MPBLABX console (that feature is not present in AS4, not sure about AS6).

No RSTDISBL, no fun!

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

Brutte wrote:
OP wrote:
if is true that with MPLAB one can use the appropriate printf() function in a PIC. Is it true?

Of course. As mentioned, similar with gcc for AVRs.
I have used XC8 and SDCC PIC toolchains under MPLABX and printf() works seamlessly on both. What is more, if you run the MPLABX PIC simulator, then output of printf() is redirected to an MPBLABX console (that feature is not present in AS4, not sure about AS6).

Cool.
I saw an example about printf with mplab. He didn't used the FILE macro as we did. Is it possible?

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

Quote:
Is it possible?

MPLAB/MPLABX is just an IDE(like Eclipse or VS). It does not printf. It is the library that does the printf job. If you want to know the details of the libraries provided with XC8/C18/SDCC/redlib/newlib/avrlibc, whatever, for this or any other toolchain then you should search the documentation.
I do not remember the details of XC8's style, but with SDCC it is very much the same as with AVRs. You must initialize USART registers, FILE* stdout stream variable:

    //Configure printf to work with USART
        usart_open( USART_TX_INT_OFF & USART_RX_INT_OFF & USART_BRGH_HIGH & USART_SINGLE_RX & USART_EIGHT_BIT & USART_ASYNCH_MODE, 1249);
        stdout = STREAM_USER;   //initialize FILE* stdout
        printf("\n\rUser stream on UART0 at 9600, 8N1 \n\r");

and provide some kind of putchar() implementation (polled or via IRQs)

No RSTDISBL, no fun!

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

clawson wrote:
If you look to the Solution Explorer and right click libraries as shown in the first picture below you should reach the dialog in the second picture. Make sure the printf_flt option is selected. Then in the Project Properties under AVR/GNU Linker look for "General" and in that make sure the last option (that mentions vprintf is ticked) as shown in the 3rd picture.

Great! I've added "-lprintf_flt" in "Other Linker Flags" instead using your add library menu (which I don't have... why? I'm using AS6)

Well, before the printf stuff my code was something like 1KB. Now we are near to 4.5KB :P

Thanks again.

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

Quote:
(that feature is not present in AS4, not sure about AS6).

AS6 doesn't do it either. As you say it's not really something supplied by the IDE (though that has to have an interface to hook into). It requires a copy of libc.a with a special implementation of printf() that, instead of outputting to LCD or UART or whatever outputs to some stream that can be input to the debugger UI. Such a "hosted lib" does not exist for AVR8 though I suppose it's within Atmel's remit to add both the AVR and IDE side of things if they wanted to offer it assuming the JTAG protocol implemented by Atmel's tools has a supporting comms channel in that direction (though again maybe it could be added to the firmware of the various ICEs?).

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

Someone knows why when I put from keyboard a "\n" or a char in general in the terminal, the code above is looping?

Here the code (running on atmega328 with AS6):

scanf("%d %d;", &calib, &calib_amb);
	printf("\n ddc %d", calib);
	printf("\n str %d", calib_amb);

I remember that on PC I solved this using sscanf parsing whenever I've wrote in stdin to a string and working on it.
But here, there's a different way from this, to receive from a more "secure" terminal? For the terminal I'm using the arduino one because I'm lazy and seems to work well :D (it works, except for this bug, when I set the autosend of "\n" when I press Enter). But as I said, if I write a "\something" or a char, is start looping printing always the last values of calib and calib_amb.

As the same way, when I wrote:

scanf("%1.2f %1.2f", &calib, &calib_amb);

	printf("\n ddc %1.2f", calib);
	printf("\n str %1.2f", calib_amb);

definitely won't work, in the sense that those 2 vars are still "0.00" and is looping without sending anything. Examples on web and used in previous PC software and books (used in an exam for the polytechnic) seems to be like my code. So I think that is something with stdin vector recalled with posts above...

Clarifications are welcome.

EDIT: I solved with that

scanf("#&37;s", incoming);
	calib = atof(incoming);
	scanf("%s", incoming);
	calib_amb = atof(incoming);

But I still wondering what is wrong...

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

Meanwhile, I noticed that (I was thinking that this error wasn't present before): at line

static FILE mystdout = FDEV_SETUP_STREAM(USARTWriteChar, USARTReadChar, _FDEV_SETUP_RW);

AS6 gives "Initialization from incompatible pointer type" but everything is working well.

NOTE: I've to say that I receiving nonsense warning, i.e. from eeprom that receive an int intead of float* (but address should be an int, and in fact it is still working correctly) and removing the "static" from the FILE row mentioned above the eeprom (which don't use the IO at all) stop to give errors. Of course, the warning of the FILE row is still appearing. And of course, with re-inserting the "static" the eeprom is still w/out warnings. wtf? Damn it, I'm an electronic, not a programmer xD Is AS6 compiling basing on its humor?

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

Quote:

"Initialization from incompatible pointer type"

Either one of USARTWriteChar or USARTReadChar really does not have the same function pointer type as expected by the struct initialisation or the copy seen at the time of invocation of FDEV_SETUP_STREAM is wrong. For example if I write:

float USARTWriteChar(int, long, char);
short USARTReadChar(char *, int, float);

static FILE mystdout = FDEV_SETUP_STREAM(USARTWriteChar, USARTReadChar, _FDEV_SETUP_RW); 

Then I get:

C:\Documents and Settings\asl\My Documents\Atmel Studio\test\test\test.c(8,15): initialization from incompatible pointer type [enabled by default]
C:\Documents and Settings\asl\My Documents\Atmel Studio\test\test\test.c(8,15): (near initialization for 'mystdout.put') [enabled by default]
C:\Documents and Settings\asl\My Documents\Atmel Studio\test\test\test.c(8,15): initialization from incompatible pointer type [enabled by default]
C:\Documents and Settings\asl\My Documents\Atmel Studio\test\test\test.c(8,15): (near initialization for 'mystdout.get') [enabled by default]

that's because the functions have the wrong interface. If I correct that to be:

int USARTWriteChar(char, FILE *);
int USARTReadChar(FILE *);

static FILE mystdout = FDEV_SETUP_STREAM(USARTWriteChar, USARTReadChar, _FDEV_SETUP_RW);	

all the warnings go away. The point being that the write/put function must return int and take a char and a pointer to FILE while the read/get function must return an int and take a pointer to FILE.

Reviewing this thread I now see the error. I had suggested:

void USARTWriteChar(char data, FILE *stream)
{
  ...
} 

that is incorrect. It should be:

int USARTWriteChar(char data, FILE *stream)
{
  ...
} 

The routine should return 0 if the character could be written or non-zero if it could not. I guess this might occur if, for example, you implemented a time-out within the function and the UART did not become ready before the time-out occurred. In the absence of such a mechanism just always return 0. That is:

int USARTWriteChar(char data, FILE *stream)
{
  ...
  return 0;
} 

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

clawson wrote:
The user manual:

http://www.nongnu.org/avr-libc/u...

The example in user manual (for which John also posted the files above):

http://www.nongnu.org/avr-libc/u...

I was wondering how streams interact with my uart (or whatever) IO function. EEverything is explained in those pages and can be applied, ok. But if I want to know just how the FFILE*stream argument interact with the argument char of usartreadchar(), for example, should I learn how those "Operating System" function work? (of course I'm talking about to those declarations that I need to implement, since there's no OS on device). Can I know if in code below, if there's an upper function that call char to *stream, since there no clear relation with those 2 arguments.

  #include 

    static int uart_putchar(char c, FILE *stream);

    static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
                                             _FDEV_SETUP_WRITE);

    static int
    uart_putchar(char c, FILE *stream)
    {

      if (c == '\n')
        uart_putchar('\r', stream);
      loop_until_bit_is_set(UCSRA, UDRE);
      UDR = c;
      return 0;
    }

    int
    main(void)
    {
      init_uart();
      stdout = &mystdout;
      printf("Hello, world!\n");

      return 0;
    }

Who tells the uart_putchar to put c to stream? Who's the witch that done that? Should her burn? I hope not!
I understand that &mystdout is a reference to a stream that uses uart_putchar and is called for every char present in printf (since printf() manipulates stdout stream initilizet at bottom of the code, as defined in stdio.h, I think). But when we go to HW level, like using UDR0, I just lose the relation bitween &mystdout, printf and FILE *stream (in which should be a parameter coming from &mystdout) but not present in uart_putchar() or whatever...

I repeat: I don't need help to implement this, since I understand how to do this, but I ask if possible if I can have a little help to understand what is hidden in the code.

EDIT: @clawson: I saw just now what you just answered. Yeah, you're right and thanks.
But I noticed something weird: I've compiled with those errors, make modifications as you said, everything work. But sometimes, indipendently if I've modified code or not, gives me same init error, bringing me to this line: (but still work)

static FILE mystdout = FDEV_SETUP_STREAM(usartwritechar, usartreadchar, _FDEV_SETUP_RW);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

§Look at this as an excellent opportunity for an exploratory adventure!

The sources for avrlibc are perfectly accessible here: http://savannah.nongnu.org/proje...

Start with looking at what hides behind FDEV_SETUP_STREAM and then look at what printf does (or rather sprintf, but you will discover that yourself). These are the two things that you actually utilize in your source, and there is no witchery in the avrlibc sources. It might utilize some aspects of C that you have not encountered (I'm sure you'll hit "function pointers" for this stream case), but everything you do not understand yet is an opportunity to learn something new!

:D

EDIT:

Quote:
Who tells the uart_putchar to put c to stream?

No-one. You got that wrong. "The stream" tells some putchar to put the character somewhere. In the case here it is something inside printf that tells uart_putchar to output the character through the UART.

Soewhere inside printf (or sprintf, or...) there is code that picks up the FILE stdout. This (as an examination of the type FILE and the macrto FDEV_SETUP_STREAM will reveal) is a struct that holds pointers to the put and get functions (and some other stuff, like the flags you send as a third parameter to FDEV_SETUP_STREAM). When you call prints it will evetually lead up to a piece of library code that picks up stdout, get the pointer to the put-function and call it for each character printf needs to output.

Again, the details are in the avrlibc sources, which I linked to above.

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]

Last Edited: Thu. Jul 25, 2013 - 08:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I forget one thing:
Functions like ReadString and WriteString, without stream initialisation, in your opinion, for a basic point of view, are fine? (they actually work)

EDIT: thanks JohanEkdahl, I've read your post after. You lead me to a foundamental conclusion. That the second argument (FILE *) is not essential to the function but only to the initialization of stream and print/scanf for some correct rule. The readchar(char, FILE*) is called by scanf() and parsing to it the char. The FILE* could be there only because printf (& Co) are made to parse a FILE pointer, even if it is not used (so we can compile w/out errors).

Am I slighty right?

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

Some time back in another thread of mine Cliff posted this snippet for me:

Quote:
// Interrupt EXT1 - RS232 TX to pc
ISR(INT1_vect){
int uart_putchar(char c, FILE *stream);
void uart_init(void);

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

I still do not understand what the "FILE uart_str ....." stuff,

and the
int uart_putchar(char c, FILE *stream);
really mean, but with Johans link I have some good reading to get started on.

THis thread has also helped quite a bit too.

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

Like Johan says you can answer a lot of this by studying the sources but first it probably helps to know these facts (I'll only deal with output but input is generally symmetric with this):

1) highlevel functions like printf and puts are all ultimately built on top of putcchar().

2) putchar is simply:

#define 	putchar(__c)   fputc(__c, stdout)

3) a FILE is basically just a struct{} that holds information about an open file. While an embedded AVR does not have filing system as such and the concept of files on disk (though this can be added with things like an SD/MMC card) just as on your PC where COM3 is effectively a "file" so the UART on your AVR can also be treated as a file. At the end of the day a "file" is nothing more than a serial stream of data anyway (in C++ the concept of filing is even called serialization) so a common structure can hold the information for any of these things. The definition of FILE is:

/*
 * This is an internal structure of the library that is subject to be
 * changed without warnings at any time.  Please do *never* reference
 * elements of it beyond by using the official interfaces provided.
 */
struct __file {
	char	*buf;		/* buffer pointer */
	unsigned char unget;	/* ungetc() buffer */
	uint8_t	flags;		/* flags, see below */
	int	size;		/* size of buffer */
	int	len;		/* characters read or written so far */
	int	(*put)(char, struct __file *);	/* function to write one char to device */
	int	(*get)(struct __file *);	/* function to read one char from device */
	void	*udata;		/* User defined and accessible data. */
};

Note towards the end there two function pointer definitions for put and get functions.

4) When you use FDEV_SETUP_STREAM it is defined as:

#define FDEV_SETUP_STREAM(p, g, f) \
	{ \
		.put = p, \
		.get = g, \
		.flags = f, \
		.udata = 0, \
	}

So when you write:

FILE mystdout = FDEV_SETUP_STREAM(USARTWriteChar, USARTReadChar, _FDEV_SETUP_RW); 

what you are really saying is:

static struct __file mystdout = {
 .put = USARTWriteChar,
 .get = USARTReadChar,
 .flags = (0x0001|0x0002),
 .udata = 0, 
};

(BTW note you don't need "static" above as it's provided as part of "FILE"). This is creating a FILE structure in the RAM of your AVR and is setting the put and get function pointers to your provided functions.

5) stdout is just a pointer to FILE that's held in RAM which is why you use:

stdout = &mystdout;

6) finally understand what fputc() does:

int
fputc(int c, FILE *stream)
{

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

	if (stream->flags & __SSTR) {
		if (stream->len < stream->size)
			*stream->buf++ = c;
		stream->len++;
		return c;
	} else {
		if (stream->put(c, stream) == 0) {
			stream->len++;
			return c;
		} else
			return EOF;
	}
}

Remember that the flags were set to 0x0001 | 0x0002 which are found in:

#define __SRD	0x0001		/* OK to read */
#define __SWR	0x0002		/* OK to write */
#define __SSTR	0x0004		/* this is an sprintf/snprintf string */
#define __SPGM	0x0008		/* fmt string is in progmem */
#define __SERR	0x0010		/* found error */
#define __SEOF	0x0020		/* found EOF */
#define __SUNGET 0x040		/* ungetc() happened */
#define __SMALLOC 0x80		/* handle is malloc()ed */

So __SWR is set. Therefore the first if() test is non-zero and the function does not return. __SSTR is not set as you want output to the put() function, not a buffer in memory so the second if() clause is skipped. It therefore will execute the final else() clause. So it calls the stream->put() function.

You now have all the pieces of the jigsaw. Put them together and you will see exactly how printf("hello") works.(*)

(*) actually there's one more thing you may want to know. If you use printf("x = %d", x) the compiler calls the full version of printf() as it needs to process the placeholder and use the argument list, but if you just call printf("hello") with a fixed string the compiler is smart enough to know that it can simply use puts("hello") - so it does. None of this really matters though, they all ultimately call putchar().

EDIT: BTW looking at the source printf(...) is effectively vfprintf(stdout,...) and within vfprintf() the output is done using putc(stream, ...). So it's not exactly using putchar() but bypassing it to use putc(stream, ...) directly where stream=stdout.

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

Oh, that is why for printing and reading only chars and managing them, for simplicity (and also +-1kB less of flash used) I use my USART string functions (https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=134734). Prtinting a variable instead a only sequence of chars, is recalling the more complex functions, as you said, so for now I exploit this to avoiding headache.

Clawson, without your help I couldn't understand that type of documentation. Thank you. At least I know where to start.

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

I stayed up too late last night to dig the sources and write what is in this and the next post. I'd be happy if the OP, Jim and Cliff (with their different experiences) look this through for things less clear and the obligatory typos. I'll move it to the Tutorials forum eventually.

Here is the first part (the "howto"):

Getting printf working [gcc/avrlibc]

This is a tutorial [in labour] on how to get print working in avr-gcc/avrlibc.

It is a short exemplified alternative to the primary documentation which you can find e.g. here:

Introduction
Out of the box avr-gcc will not direct printf output so any specific place. It can not know if you want the output to e.g. the UART or to an LCD diplay or.. What printf supplies is all the funky stuff needed to convert different kinds of data to displayable format (i.e. converting it so strings) and a generic infra-structure to send it to some output hardware. What you will need to supply is the last link in the chain - something that outputs a character to the place where you want it to go.

You do this by

1. Writing an output function. It must always have the following prototype:

int putchar_function(char c, FILE *stream)

You can give any name you want to this function, but it must follow the prototype above regarding parameters and return type.

2. Create a FILE variable (using the predefined FDEV_SETUP_STREAM macro) and make the predefined stdout variable point to it.

These two rather simple things is all there is to it.

Example
This is a slightly reworked variant of the example in the avrlibc documentation.

The output function takes the character it receives as an input parameter and sends it through the UART:

    int uart_putchar(char c, FILE *stream)
    {
      // Wait until the UART is free
      loop_until_bit_is_set(UCSRA, UDRE);
      
      // Send the character through the UART
      UDR = c;
      
      // Done!
      return 0;
    }

Since we are using the UART it of-course needs to be initialized, set to transmit at a certain speed etc. This is not shown here.

<<Perhaps finish off with a complete program for e.g. the ATmega328>>

Now that we have an output function we can use that to set up the stream. This process is simplified by using a macro FDEV_SETUP_STREAM that is predefined in avrlibc. The macro takes three parameters:

FDEV_SETUP_STREAM(put, get, rwflag)

The first parameter put is the putchar function you have written.

The second parameter is used if you want input streams using e.g. scanf. In that case you need to write a getchar function, just like you wrote a putchar function for the output stream. The third parameter is used to tell avrlibc if you want the stream to handle output, input or both.

The macros is used to initialize a FILE variable. In our example it goes like this:

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

The _FDEV_SETUP_WRITE tells avrlibc that we want only output through the stream. Since we do not want any input we have written no getchar functino, so we pass NULL as the second partameter.

The last thing we need to do is to set stdout (the predefined standard output stream ) to point to our FILE variable:

stdout = &my_stdout;

With this we can now code things like

printf("Hello stdio!");

or

int meaning_of_life = 42;
printf("The meaning of life is #i", meaning_of_life);

[TOBE]Here is a complete program, for an ATmega328, doing what is described above:

<<Complete example>>
[/TOBE]

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

Behind the scenes of printf [gcc/avrlibc]

How does does the streams in avrlibc work? What goes on when you do things like

int meaning_of_life = 42;
printf("The meaning of life is #i", meaning_of_life);

?

Based on the previous tutorial, we'll now look behind the scenes.

IMPORTANT NOTE ON PREREQUISITES: To fully understand this you will need to know about the C concept of "function pointers". Without understanding those parts of this tutorial might not make sense. This tutorial will simply assume you already have that knowledge. It is outside of the scope of this tutorial to teach "function pointers in C".

The overall structure
[TODO]Some work needed here[/TODO]
Application --> printf and friends --> putchar --> hardware peripheral

Expanding FDEV_SETUP_STREAM
The FDEV_SETUP_STREAM macro is defined like this in avrlibc:

#define FDEV_SETUP_STREAM(p, g, f) \
	{ \
		.put = p, \
		.get = g, \
		.flags = f, \
		.udata = 0, \
	}

So when you code

FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE)

the compiler will actually see

	{
		.put = uart_putchar,
		.get = NULL,
		.flags = _FDEV_SETUP_WRITE,
		.udata = 0,
	}

What we actually did in our example was

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

and after macro expansion the compiler will see this:

FILE my_stdout = {
                   .put = uart_putchar,
                   .get = NULL,
                   .flags = _FDEV_SETUP_WRITE,
                   .udata = 0,
                 }

To understand this, we need to look at how the type FILE is actually is defined:

#define FILE	struct __file

So, now we need to take a look at the __file struct definition. I show it here somewhat "cleaned up" as compared to the actual definition in the sources:

struct __file {
	char	*buf;		/* buffer pointer */
	unsigned char unget;	/* ungetc() buffer */
	uint8_t	flags;		/* flags, see below */
	int	size;		/* size of buffer */
	int	len;		/* characters read or written so far */
	int	(*put)(char, struct __file *);	/* function to write one char to device */
	int	(*get)(struct __file *);	/* function to read one char from device */
	void	*udata;		/* User defined and accessible data. */
};

This struct keeps all the essential data that avrlibc needs to keep about a certain stream. Not all members of the struct are used in every scenario, but you should note that among the members in this struct there are some whose names should look slightly familiar: flags, put, get and udata.

We are finally done, and the result of our digging is this: The code

FILE my_stdout = {
                   .put = uart_putchar,
                   .get = NULL,
                   .flags = _FDEV_SETUP_WRITE,
                   .udata = 0,
                 }

creates a __file struct and initializes some of its members. The thing of central interest to us is that the member put (which is a function pointer) is initilized to point to our uart_putchar function.

Dissecting printf
Now we can turn to the call to printf. Remember the meaning of life..

int meaning_of_life = 42;
printf("The meaning of life is #i", meaning_of_life);

When we call printf it will actually call another function internal to avrlibc: vfprintf. Anyone calling vfprintf must pass as a parameter the stream which vfprintf should use, and printf passes the C standard output stream, stdout. Yes, the very one we did set up above!

The vfprintf function will in turn do essentially two things:

1. Use the parameters (in our case the format string, and one additional parameter) into one string. I will not go into details on this here since it is not central, but the end result is the string "The meaning of life is 42".

2. Take the characters, one by one, and call a function putc. This putc function takes two parameters: The stream to use, and one character to put to that stream.

We now encounter some tricky stuff, but for our purposes we do not need to dig into the details: The putc function is actually written in assembler. It is trivial. All it does is to pass on control to yet another function calle fputc. This is an ordinary C function. It contains some code for maintaining information on the "inner state" of a stream, and some error handling. Here it is in full:

int fputc(int c, FILE *stream)
{

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

	if (stream->flags & __SSTR) {
		if (stream->len < stream->size)
			*stream->buf++ = c;
		stream->len++;
		return c;
	} else {
		if (stream->put(c, stream) == 0) {
			stream->len++;
			return c;
		} else
			return EOF;
	}
}

For our purposes, understanding how the bits and pieces of stream output all fit together, we can actually reduce it to this:

int fputc(int c, FILE *stream)
{
   if (stream->put(c, stream) == 0) {
      stream->len++;
      return c;
   } else
      return EOF;
}

The absolutely central part of this is

stream->put(c, stream)

The stream parameter has come all the way from when printf called vfprintf and in that case it is actually stdout. Now, remember that we set it to point to a struct we created, and that this struct has a member called get. That member is a function pointer which refers to the my_putchar that we wrote.

Finally we are at the end of the road!

stream->put(c, stream)

actually calls our my_putchar. Our code in that function outputs the character to the UART.

Now, you might wonder why the putchar function actually needs the FILE parameter. We certainly are not using it in our implementation. The answer is that such things happen in a "framework" where things will be used in different ways at different times. Other implementations might want to do some manipulation of the FILE structure. Making it a parameter allows these implementations to do so. Our implementation does not oeed to, but still has to receive the parameter. We simply will not touch it after that.

Phew! If you at this time is somewhat confuse, or feel your head is spinning, then that is quite okay. Take a break and then do something else. Return here in a day or three and re-read. Hopefully things will clear up a bit with a few re-reads.

And if you still have no clue about "function pointers in C" then you have chose the wrong order of learning things. Don't get me wrong here! You can still use output streams. You just can't understand how they function internally.

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

And a little addition about code size using various forms of printf

#include 
#include 
#include "project_definitions.h"

#include "USART0_s.h"
#include "USART1_s.h"

#define PI 3.141593


FILE com0_str = FDEV_SETUP_STREAM(usart0_putchar, usart0_getchar, _FDEV_SETUP_RW); 
FILE com1_str = FDEV_SETUP_STREAM(usart1_putchar, usart1_getchar, _FDEV_SETUP_RW); 

void printf_test_init (void) 
{ 
   stdout = stdin = com0; 
   usart0_init(); 
   usart1_init();
} 

int	main(void)
{
	printf_test_init();
	
//No floating point support
	printf("Printing on com 0\r\n");								// Using stdout	600 bytes
	fprintf(com0, "Printing on com 0 with fprintf\r\n");			// 780 bytes
	fprintf(com1, "Printing on com 1 with fprintf % d\r\n",12345);	// 2250 bytes

//With floating point support
	fprintf(com0, "Printing on com 0 with fprintf % f\r\n",1.23456);	// 3882 bytes
	fprintf(com1, "Printing on com 1 with fprintf % f\r\n",PI);		// 3972 bytes

	for(;;);
}

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Johan, I will have to look at this again in the morning. From what I just read I think I hurt myself. ;)

YOu might want to change 'prototype' :

Quote:

1. Writing an output function. It must always have the following prototype:
Code:
int putchar_function(char c, FILE *stream)

You can give any name you want to this function, but it must follow the prototype above regarding parameters and return type.

To 'PROTOCOL', maybe?

I will follow protocol ;)

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

jgmdesign wrote:
YOu might want to change 'prototype' :
Quote:

1. Writing an output function. It must always have the following prototype:
Code:
int putchar_function(char c, FILE *stream)

You can give any name you want to this function, but it must follow the prototype above regarding parameters and return type.

To 'PROTOCOL', maybe?
Surely not.
It IS a function prototype, "protocol" would be totally inappropriate in this case.
It is like asking him for replacing "steering wheel" by "handle" in a tutorial about car driving (replacing the correct specific name by something that is distantly related).

Stefan Ernst

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

@Johan: I'm glad that something new useful is born from my thread. Thank you for help.

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

Stefan
In my world a prototype is the 'test unit' and usually has bugs in it where as a 'protocol' is a strict set of rules to follow. Like a communications 'protocol'.

Thanks for explaining

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:

In my world a prototype is the 'test unit'

That might be so, but you have entered "the C world". "Function prototype" is well established C terminology. A "function head without a body" is called a "function prototype". It is not something I made up:

http://en.wikipedia.org/wiki/Fun...
http://www.cprogramming.com/tuto...
http://cplus.about.com/od/glossa...
http://www.codeproject.com/Artic...
.
.
.

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

"Prototype" in programming is the documentation of interface to a function. In general .h files are full of extern variable declarations and function prototypes. It's simply a different meaning of the word which also means "first test version" when applied to electronics/computer hardware.

I suppose you could use the term "function declaration" instead of "prototype" or even just "API".

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

Quote:

I suppose you could use the term "function declaration"

Or "funtion head", or "function header" perhaps. But "prototype" is the first term for this in my world.

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 hear what you are saying all of you. I was merely posting an opinion based on Johan asking that the OP myself and Cliff take a look at his budding tutorial

Sorry I said anything

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:

In my world a prototype is the 'test unit' and usually has bugs in it where as a 'protocol' is a strict set of rules to follow.

Let's look at a couple of dictionary-type definitions:
Quote:
pro·to·type (prt-tp)
n.
1. An original type, form, or instance serving as a basis or standard for later stages.
2. An original, full-scale, and usually working model of a new product or new version of an existing product.
3. An early, typical example.
4. Biology A form or species that serves as an original type or example.
...
Adj. 1. prototypical - representing or constituting an original type after which other similar things are patterned; "archetypal patterns"; "she was the prototypal student activist"
...
Definition of PROTOTYPE
1: an original model on which something is patterned : archetype
2: an individual that exhibits the essential features of a later type
3: a standard or typical example
4: a first full-scale and usually functional form of a new type or design of a construction (as an airplane)

While "early" and "working model" are indeed mentioned, where is the implication of "buggy"? The first definition, from two sources, seems to perfectly describe the C function prototype, and the standards-makes seem to have picked the correct word to describe the facility.

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: 0

Quote:

where is the implication of "buggy"?

Maybe not but have you ever created a (physical) prototype that didn't expose at least one flaw in either the electronic or software design? ;-)

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

Well, I have been making some progress, but I am back at confused.

I can get the LCD to init. I get the screen to clear, and the cursor in the upper left so the comms are good. The fprinf is where I am stuck. Just want to see hello world and stop.

I also get two warnings as well as seen below:

#include 
#include 
#include 
#include "lcd.h"


FILE lcd_str = FDEV_SETUP_STREAM(lcd_puts, NULL, _FDEV_SETUP_WRITE);
void avrinit(void)
{
	DDRA = 0xFF;	//porta all outputs for LCD
}
int main(void)
{	avrinit();
	lcd_init(LCD_DISP_ON_CURSOR_BLINK);
	stdout = &lcd_str;
	
	
	fprintf(stdout,"Hello world!\n");
    while(1)
    {
        //TODO:: Please write your application code 
    }
}

Mega2560 @4MHZ

Here is the warning:

Attachment(s): 

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

Could not edit the above post so I'll continue here.

I cannot for the life of me figure out the FILE xxx = FDEV etc.

So I took some code out of TEST_LCD and it worked.

Here is my new file:

#include 
#include 
#include 
#include 
#include "lcd.h"



void avrinit(void)
{
	DDRA = 0xFF;	//porta all outputs for LCD
}
int main(void)
{	avrinit();
	lcd_init(LCD_DISP_ON_CURSOR_BLINK);
	
	 /* put string to display (line 1) with linefeed */
	 lcd_puts("LCD Test Line 1\n");

	 /* cursor is now on second line, write second line */
	 lcd_puts("Line 2");
    while(1)
    {
        //TODO:: Please write your application code 
    }
}

Works for now, but I am going to have to re-read Johans posts a few more times

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

In your program two posts above a declaration or definition of lcd_puts is missing.

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

Surely there'd be a declaration in lcd.h

The warning about incompatible pointer will be because the interface is different. I bet the lcd_putchar is not designed to take a stream pointer as the second parm (even if it isn't used).

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

You only need these lines of code:

#include "lcd.h"

int main(void)
{
   lcd_init(LCD_DISP_ON_CURSOR_BLINK);
   /* put string to display (line 1) with linefeed */
   lcd_puts("LCD Test Line 1\n");

   /* cursor is now on second line, write second line */
   lcd_puts("Line 2");
   while(1)
   {
       //TODO:: Please write your application code
   }
}

1. avrinit() is superfluous. lcd_init() does the initalisation
2. since we are not referencing DDRA, we don't need
3. since we are not referencing printf etc, we don't need
4. since we are not referencing uint8_t etc, we don't need
5. since we are not referencing PROGMEM etc, we don't need

Untested.

There is absolutely no harm in including those standard header files. I just thought it useful to point out the simplicity of a module program.

Personally, I would try to use functions rather than the lcd specific functions.

#include 
#include 
#include 
#include "lcd.h"

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

int main(void)
{
   lcd_init(LCD_DISP_ON_CURSOR_BLINK);
   stdout = &stdlcd_struct;
      
   fprintf(stdout, "Hello world!\n");
   while(1)
   {
      //TODO:: Please write your application code
   }
}

Also Untested. It should 'work' but give a warning about the type of lcd_putchar(). i.e. you could use an intermediate " FILE* " version of lcd_putchar()

David.

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

However some of those headers will be required to make your stdio/fprintf version work. BTW you do know that printf() is shorthand for fprintf(stdout,...)?

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

Sorry, it was naughty to post Untested code. So I have added the 'dummy' function to keep the Compiler happy:

#include 
#include 
#include 
#include "lcd.h"

// dummy "std" version of the LCD output function
int stdlcd_putc(char c, FILE *fp)
{
	lcd_putc(c);
	return 0;      // seems to want 0 for GOOD
}

FILE stdlcd_struct = FDEV_SETUP_STREAM(stdlcd_putc, NULL, _FDEV_SETUP_WRITE);

int main(void)
{
	lcd_init(LCD_DISP_ON_CURSOR_BLINK);
	stdout = &stdlcd_struct;
	
	fprintf(stdout, "Hello world!\n");
	while(1)
	{
		//TODO:: Please write your application code
	}
}

Don't get me wrong. Including loads of standard headers does no harm. I just thought that I would show what a Compiler needs. i.e. sufficient info about external functions and structures.

Note that you can use several equivalent sequences:

// longwinded way of doing things:
	stdout = &stdlcd_struct;
	fprintf(stdout, "Hello world!\n");

// print to the 'regular' standard output stream:
	stdout = &stdlcd_struct;
	printf("Hello world!\n");

// just print to a specific stream:
	fprintf(&stdlcd_struct, "Hello world!\n");

David.

Last Edited: Sun. Aug 4, 2013 - 11:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
3. since we are not referencing printf etc, we don't need

Not in the latest attempt but scroll up to see that the ultimate goal here seems to be to get (f)printf working.

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 have subsequently tested the code examples with AS6 that I posted earlier.

And you don't need any other header files. Each module "knows" what is necessary to compile. The linker puts it all together.

David.

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

JohanEkdahl wrote:
Quote:
3. since we are not referencing printf etc, we don't need

Not in the latest attempt but scroll up to see that the ultimate goal here seems to be to get (f)printf working.

Well the ultimate goal was to actually get printf, but where I started getting confused was in the STDIODEMO project there were a couple of lines that I thought answered my questions on how do you output to an LCD, or to the UART when you have both connected.

FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
FILE lcd_str = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);
.
.
.
.
stdout = stdin = &uart_str;
  stderr = &lcd_str;

This leads me to believe that you can have two outputs. One for the UART, and the other for a local LCD. But lets forget about that right now. I have been concentrating on the LCD stuff, along with a pushbutton, but thats for another thread.

Here is what I have right now and it indeed does work:

#include 
#include 
#include 
#include 
#include "lcd.h"



void avrinit(void)
{
	DDRA = 0xFF;	//porta all outputs for LCD
	
}
int main(void)
{	avrinit();
	lcd_init(LCD_DISP_ON_CURSOR_BLINK);
	
	 /* put string to display (line 1) with linefeed */
	 lcd_puts("LCD Test Line 1\n");

	 /* cursor is now on second line, write second line */
	 lcd_puts("Line 2");

while(1)
{
          
}
}

I get two lines that read exactly like what is in the code. I was simply trying to use the printf statement and ran into some issues that I will have to work out with the advice given above.

What is my short term goal? To get printf to work sending text to an lcd. Then to the UART, which I have done already, and then finally a selective where depensing on STDOUT, or STDERR pick which way the data goes. I guess I could simply use printf for the uart and keep lcd_puts for the LCD. Probably much easier.

The long term is to have a pushbutton that will change what is on the display. This is what I was working on late least night with Deans Bit manipulation tutorial.

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

Quote:
This leads me to believe that you can have two outputs
..or more one for each usart (say 4 for big chips), one for LCD etc.

I'm using the lcd files from stdiodemo in my current project https://www.avrfreaks.net/index.p...
and added to lcd.h

#define lcd (&lcd_str)
extern FILE lcd_str;

and in my init file

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

I did something similar on a project where I used my own display pcb with 5 7x5 led displays (just had to supply the display output function)

#define display (&display_str)

extern FILE display_str;
.
.
.
	FILE display_str = FDEV_SETUP_STREAM (display_putchar, NULL, _FDEV_SETUP_WRITE);

And in another project with 4 usarts

	FILE com0_u_str = FDEV_SETUP_STREAM (NULL, usart0_unbuffered_getchar, _FDEV_SETUP_READ);
	FILE com0_str = FDEV_SETUP_STREAM (usart0_putchar, usart0_getchar, _FDEV_SETUP_RW);

	FILE com1_u_str = FDEV_SETUP_STREAM (NULL, usart1_unbuffered_getchar, _FDEV_SETUP_READ);
	FILE com1_str = FDEV_SETUP_STREAM (usart1_putchar, usart1_getchar, _FDEV_SETUP_RW);

	FILE com2_u_str = FDEV_SETUP_STREAM (NULL, usart2_unbuffered_getchar, _FDEV_SETUP_READ);
	FILE com2_str = FDEV_SETUP_STREAM (usart2_putchar, usart2_getchar, _FDEV_SETUP_RW);

	FILE com3_u_str = FDEV_SETUP_STREAM (NULL, usart3_unbuffered_getchar, _FDEV_SETUP_READ);
	FILE com3_str = FDEV_SETUP_STREAM (usart3_putchar, usart3_getchar, _FDEV_SETUP_RW);

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Pages