printf (& other stdio functions) & dual UARTS

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

Hi All,
I have used various stdio functions on an ATMega128,
but now I have a need to send data to both UART 0 & 1.

At this time I close on stream & then open the other
and then back again.
How does one go about calling stdio functions on both streams without closing & opening streams?

Regards,
Lee

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

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

You can associate as many streams as you want with your respective
backend functions. You just have to use the "f" functions then
(i.e. those starting with the letter "f"). Remember that printf(...)
is just a shorthand for fprintf(stdout, ...).

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

OR..

#define com1_in (__iob[3])
#define com1_out (__iob[4])

then

	FILE com1_str = FDEV_SETUP_STREAM(usart1_putchar, usart1_getchar, _FDEV_SETUP_RW);

and

	com1_out = com1_in = &com1_str;

finally something like

	fprintf ( com1_out, "%c%c%c\r", VMUSIC2_set_volume_cmd1,
				VMUSIC2_set_volume_cmd2, (dB * 2));

or

	putc ('\r', com1_out);

...it's good to be a C guru.. :lol:

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

How to mysteriously crash your embedded application, part #42:

> #define com1_in (__iob[3])
> #define com1_out (__iob[4])

John, these things start with two underscores for a reason: unless
documentation tells you to do so, you are /not/ supposed to ever use
anything like this. Never. In this case: __iob[] has just three
elements, and it is only supposed to hold the handles for stdin,
stdout, and stderr, respectively.

The remainder of your posting is OK.

Btw., there's no reason to have separate handles for input and
output direction. (You don't really have them but make your code
look like they were needed.)

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

I'm intregued by this method of talking to the UARTs. Is there some full demo code somewhere?

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

Quote:

Is there some full demo code somewhere?

You mean like in \doc\avr-libc\examples\stdiodemo?

Alternative navigation path is the avr-libc documentation, Example Projects, comprehensive example on using the standard IO facilities.

When everything else fails, read the documentation...

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

Quote:
How to mysteriously crash your embedded application, part #42:

> #define com1_in (__iob[3])
> #define com1_out (__iob[4])

So does that make me a goo instead of a guru?? :lol:

Without the defines I get so many errors and I use the defines without problems. I just looked at the way stdio does it and added 2 more iob thingoes...

So what is the correct way of doing multiple USARTs, 2 or even 4 for chips that have them.

I was so proud of getting the second USART working, almost effortlessly, without asking anyone...I'm SOOOO shattered now.

Quote:
there's no reason to have separate handles for input and
output direction.
I followed what the examples shows for USART0 in stdiodemo.c.

Can we please have another demo with mutiple USARTs added to the examples? (I may have to switch to a chip with 4 USARTs for a project and I'm sure everyone would want to hold me fast to the dark side)

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Quote:

So does that make me a goo instead of a guru??

[Given a recent thread on the Off-Topic forum, perhaps a 'Roo rather than guru.]

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

A goo-roo then :?

CVAVR has another approach in their demo, not as elegant as the way I did did though. :wink:

// Define the new putchar function
void putchar(char c)
{
switch (poutput)
       {
       case USART0: // the output will be directed to USART0
            while ((UCSR0A & DATA_REGISTER_EMPTY)==0);
            UDR0=c;
       break;

       case USART1: // the output will be directed to USART1
            while ((UCSR1A & DATA_REGISTER_EMPTY)==0);
            UDR1=c;
       break;
       
       case LCD:    // the output will be directed to the LCD
            lcd_putchar(c);
       };
}
.
.
   // printf output will be directed to the LCD
   poutput=LCD;
   printf("The LCD has %u\ncharacters/line.",LCD_CHARS_LINE);
.
.
   // putsf output will be directed to USART0
   poutput=USART0;
   putsf("The LCD is not present.\r");
.
.
// printf output will be directed to USART1
poutput=USART1;
printf("This is a test of USART%u.\r",1);

I can't even see an example with Imagecraft.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

To date, I've always done my CV dual-U(S)ART apps with twin drivers. The devices always seem to be dissimilar so I needed some tailoring anyway.

I guess one wants to decide, in a MICROcontroller program, how many simultaneous "files" will be used. The example from CV above would tend to break down with buffered interrupt-driven communications.

My next app, tho, will be a centipede with 4x similar USART streams, so a "generic" driver is in the works. As these are RS485 links, I hat to use the indirect access for the TE-/RE signal, as it is so inefficient but I may have no choice. I also hate to call functions via pointer in ISRs so it is TBD how to write the interrupt handlers. Hmmm--I use different buffer sizes for different needs. Maybe this will be my first AVR malloc() use as well.

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

> Without the defines I get so many errors and I use the defines without problems.

Accessing them trashes 4 bytes or RAM after the variable __iob[] (which is
statically allocated as an array of 3 pointers).

If you've got errors, then don't hesitate to post them. It is supposed to
work without touching __iob[]. The latter is really only there in order to
internally hold the handles for stdin, stdout, and stderr, respectively,
because they are required by the standard, and because stdin and stdout are
used implicitly in some of the functions.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Quote:
It is supposed to
work without touching __iob[].
I'm posting from another computer and it's late here.

How do I then define com1_in and com1_out for

com1_out = com1_in = &com1_str; 

or don't I need that line? IIRC I get undefined errors which is correct as they are not defined anywhere without the

 #define com1_in (__iob[3]) 
#define com1_out (__iob[4]) 

In other word how do I assign a stream to com1?

Tomorrow I'll compile it againg and see what knid of errors I get.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Quote:
In other word how do I assign a stream to com1?
No need to make the extra alias, you have your stream (FILE) in com1_str so you just need to provide the address when using the stdio functions (that take a FILE*) like so:

fprintf(&com1_str, "%c%c%c\r", MUSIC2_set_volume_cmd1, 
            VMUSIC2_set_volume_cmd2, (dB * 2)); 

/Lars

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

Ok, maybe you prefer the indirection, then just add your own com1 definition:

FILE *com1 = &com1_str;

/Lars

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

In the latter case, it will occupy another 2 bytes though. If
it's only your aesthetical feelings that are hurt by the & operator,
try:

#define com1 (&com1_str)
...
  fprintf(com1, "Hello, beautiful com%d world!\n", 1);

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define com1 (&com1_str)

OK thanks, so don't have to specify com1_in and com1_out. I'll try this out.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Or for those that dislike macros:

static FILE * const com1 = &com1_str;

However:

FILE *com1 = &com1_str;

does use more than 2 extra bytes, there is the code needed the read the pointer as well.

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

Confusion reign supreme here!! :(

Ok so this is what's happening. All the USART stuff is in the init files (C and H) as follows.

//Ad station mk2 initialization header file

void Ad_station_mk2_init (void);

//#define com1_in (__iob[3])	//Original worked
//#define com1_out (__iob[4])	//Original worked

#define com1 (__iob[3])		//Works

//#define com1 (&com1_str) 	//Doesn't work as com1_str in not known here yet

and in the C init file

	FILE uart_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 Ad_station_mk2_init (void)
{
#if debug==0
	MCUCR = (1<<JTD); 							//Disable JTAG in run mode
	MCUCR = (1<<JTD); 
#else
	MCUSR_status = MCUSR;						//Make a copy of MCUSR for debugging
#endif

	stdout = stdin = &uart_str;
	com1 = &com1_str;

I have changed all statements that contained com1_out to com1 and it's all working. ie

void VMUSIC2_list_file (char * file_name)
	{
	fprintf ( com1, "%c%c%s\r", VMUSIC2_list_file_cmd1,
				VMUSIC2_list_file_cmd2, file_name);
	}
.
.
void VMUSIC2_list_all_files (void)
	{
	putc (VMUSIC2_list_file_cmd1, com1);
	putc ('\r', com1);
	}

I don't have any input from com1 yet with stdio as I'm doing by just reading the UDR, I will need to eventually use stdio for input from com1 as I neeed to read a lot of data using interrupts, so I will use usart1_getchar from the USART1 driver.

Com1 is used in files outside the init file just in case anyone wonders.

I guess the op is no longer interested but now I am.. :?

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//#define com1 (&com1_str)    //Doesn't work as com1_str in not known here yet 

Actually it need not be known until it is used so I expect the error you got was at the place com1 was used.
You need to make com1_str known by declaring it in the .h file:

extern FILE com1_str;

More here:
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=48535
/Lars

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

Quote:
You need to make com1_str known by declaring it in the .h file:

Code:
extern FILE com1_str;

../Ad_station_mk2_init.h:12: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'com1_str'
with or without the semicolon. :(

I even tried the same thing in a few other files.

I guess the most important question is, why does it work the way I did it originally, if is it so wrong.

How about all you clever people post a simple program that prints to 2 (or even 4) different ports using stdio and with separate init files? It's much simpler than trying to guess what's going on. I'm simply using the standard UART.C and H files modified to run with USART and I'm posting them here.

So one file for main and another for USART init.

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

And just to make it a bit easier for everyone, here is the project to use the 2 com ports with the incorrect, but working, __iob thingoes. Please fix so it would be correct. :-)

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//	com_ports_test_init header file

#include 

extern FILE com0_str;
extern FILE com1_str;

#define com0 (&com0_str)
#define com1 (&com1_str)

void com_ports_test_init (void);

While I must say I would probably have put the above com0 and com0_str in usart0.h and the com1 and com1_str in usart1.h, instead of having them in yet another .c/.h file combo.

//INIT file
#include 	
//#include 

#include "com_ports_test_init.h"
#include "usart0.h"
#include "usart1.h"

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 com_ports_test_init (void)
{
	stdout = stdin = com0;
	usart0_init();
	usart1_init();
}

Also here, I would have put "FILE com0_str = ..." in usart0.c and "FILE com1_str =" in usart1.c, instead of defining them in com_ports_test_init.c

Stealing Proteus doesn't make you an engineer.

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

Thanks guys,
I was sure that there was a simple(?) and elegant solution . It is nice to see how much useful discussion it has generated.
Now back to my code....
Thanks heaps,
Lee

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

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

Quote:
It is nice to see how much useful discussion it has generated.
Which would have not happened if we had a multi UART example STDIODEMO. (or people just knew whwt to do :( )

Joerg needs to be sacked. :lol: :lol:

It would still be nice to know why and how my original code worked being in such a mess.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js wrote:
Quote:
It is nice to see how much useful discussion it has generated.
Which would have not happened if we had a multi UART example STDIODEMO. (or people just knew whwt to do :( )

Patches welcome! :D

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

Hi all,
Yep it works very nicely now. The avr-libc example project make much more sense to me too now.

One problem(?) that I still come get is the following warnings:

warning: initialization from incompatible pointer type
(twice)from the line
FILE com1_str = FDEV_SETUP_STREAM (uart0_putchar,uart0_getchar, _FDEV_SETUP_RW);

I have tried various casts, but have had no success in
eliminating these warnings. I had similar problem when using fdevopen(...,...).

Is it possible to get rid of these errors or do I need to put up with them?

--... ...--,

Lee

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

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

Can you show the prototype for your getchar and putchar routines?

Cliff

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

Hi Cliff,
Here they are!

char uart0_getchar(void);
int uart0_putchar(char);
char uart1_getchar(void);
int uart1_putchar(char);

Lee

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

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

But those don't follow what's given in the manual:

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

The prototype for the putchar's should be:

int    uart_putchar(char c, FILE *stream)

and for the getchar's it is:

int uart_getchar(FILE *stream)

You may want to look at how this is done in the worked example in the user manual:

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

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

Also here

FILE com1_str = FDEV_SETUP_STREAM (uart0_putchar,uart0_getchar, _FDEV_SETUP_RW); 

you are assigning com1_str to uart0 instead of uart1.

The 2 USART.c and .h files I posted are just the stdiodemo modified files for USART0 and USART1 to work with newer chips.

These are the updated files as suggested by Arnold.

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Well done Cliff! I will always be humble in your presence!
I would have avoided this problem in the first place if had used uart.c (but I would not have learnt as much either)!

Regards,
Lee

For those who may stumble accross this thread in the future, here are the salient lines.
______________________________________________________

int uart0_getchar(FILE *);
int uart0_putchar(char c, FILE *);
int uart1_getchar(FILE *);
int uart1_putchar(char c, FILE *);

FILE com1_str = FDEV_SETUP_STREAM(uart0_putchar,uart0_getchar, _FDEV_SETUP_RW);
FILE com2_str = FDEV_SETUP_STREAM(uart1_putchar,uart1_getchar, _FDEV_SETUP_RW);

#define com1 (&com1_str) //alias
#define com2 (&com2_str) //alias


//and in main

init_uarts(); //set baud rates etc.
stdout=stdin=stderr=com1;

printf("Change connector to com2 & press any key");
fgetc(com2);	//wait for key press on com2
fprintf(com2, "Good you found the any key");


& finally the low level drivers for uart n

int uartn_getchar(FILE *stream)
{
	loop_until_bit_is_set(UCSRnA, RXCn);
	return	UDRn;
}



static int uartn_putchar(char ch, FILE *stream)
{
      loop_until_bit_is_set(UCSRnA, UDREn);
      UDRn = ch;
      return 0;
}

___________________________________________________

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?