Using fgetc

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

I have a test program that, among other things, grabs a byte from 1 usart, if present, and sends it to the other.

//Do USARTs

	if (UCSR0A & (1<<RXC0))
		{
		my_char = UDR0;
		fputc (my_char, com1);
		}

	if (UCSR1A & (1<<RXC1))
		{
		my_char = UDR1;
		fputc (my_char, com0);
		}

I thought I could use fgetc to get a byte, if present, from a usart stream (com0 or com1) without an echo unless the program wants to echo it as above.

It seems to get stuck in the stdin getchar routine which grabs the bytes, echoes it back and keeps on reading from the same usart until a CR??

For all intents and purposes I'm using code similar to the stdiodemo. Streams defined as follows:

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

// Declare your global variables here

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

I think I can see why the problem happens as the second parameter is usartX_getchar for the file stream so I may juts need to write my own simple my_getc0 and my_getc1 rather than use stdio.

So basically can getc or fgetc be used in a setup like stdiodemo?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Thu. Jul 22, 2010 - 09:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It seems to get stuck in the stdin getchar routine which grabs the bytes, echoes it back and keeps on reading from the same usart until a CR??

But that's how the uart_getchar() in the stdiodemo is written? There's an infinite loop:

    for (cp = b;;)

and the only way out is either with an error:

	if (UCSRA & _BV(FE))
	  return _FDEV_EOF;
	if (UCSRA & _BV(DOR))
	  return _FDEV_ERR;

or when '\n' (could also be \r) is received and it break's:

	if (c == '\n')
	  {
	    *cp = c;
	    uart_putchar(c, stream);
	    rxp = b;
	    break;
	  }

So, usually, once uart_getchar() has been entered it can never leave until the EOL (\r or \n) is seen.

If you don't want this behaviour then you have to rewrite that uart_getchar() to just get one character at a time.

What the current one is doing is building a buffer of characters then once a line (up to the \n) is buffered it feeds back one time of the buffer at a time until the buffer is exhausted.

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

So the answer (to the final question at the bottom) is no as I suspected.

Joerg is missing out on the beer then, here I am putting those cents away for the day we meet and need to buy it in accord with the license agreement. :lol:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Just to finalise this thread and hopefully encourage someone to post a better solution (I know it must exist) I have added 2 more unbuffered streams for fgetc input.

#define com0 (&com0_str)
#define com1 (&com1_str)
#define com0_u (&com0_u_str)
#define com1_u (&com1_u_str)

int	usart1_unbuffered_getchar(FILE *stream);
int	usart0_unbuffered_getchar(FILE *stream);

	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);
	FILE com0_u_str = FDEV_SETUP_STREAM (usart0_putchar, usart0_unbuffered_getchar, _FDEV_SETUP_RW);
	FILE com1_u_str = FDEV_SETUP_STREAM (usart1_putchar, usart1_unbuffered_getchar, _FDEV_SETUP_RW);

int	usart1_unbuffered_getchar(FILE *stream)
{
	uint8_t c=0;

	if (UCSR1A & (1<<RXC1))
	{
	c=UDR1;
	}
  
	return c;
}

int	usart0_unbuffered_getchar(FILE *stream)
{
	uint8_t c=0;

	if (UCSR0A & (1<<RXC0))
	{
	c=UDR0;
	}
  
	return c;
}

now the actual USART cross print code looks like this

//Do USARTs

	if ((my_char=fgetc(com0_u)))
		{
		fputc ('*', com0);
		fputc (my_char, com1);
		}

	if ((my_char=fgetc(com1_u)))
		{
		fputc ('*', com1);
		fputc (my_char, com0);
		}

I have added asterisk printing as feedback to the active terminal.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

John,

I gotta ask - what do you see as the advantage of doing all this using file streams? Why bother with fputc/fgetc and not just simply copy bytes from UDR1 to UDR0 and vice versa?

Cliff

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

John,

If you are expecting humans to type on keyboards, they do not appreciate being told to use the 'LineFeed' key. They also appreciate seeing the keys that they type.

Try this simple function. You can adapt to getc(fp) since you are using several streams.

#include 

void getline(char buf[], unsigned char max)
{
	char c, cnt = 0;
	while (cnt < max && (c = getc(stdin)) != '\r') {
		if (c == '\b') {
			if (cnt == 0) continue;
			else cnt--;
		}
		else buf[cnt++] = c;
		putc(c, stdout);
	}
	buf[cnt] = 0;
}

You really only need a simple FILE *com1, *com2.
These will use the primitive UART functions, and not echo the UART_receive().

You do the echoing as necessary, as in my example function. You will also find it useful to have a kbhit_com0() and kbhit_com1() function.

OTOH, you may prefer to have your UART_receive() returning 0 when there is nothing received. Either way, it is always useful to have a non-blocking option.

The output functions are always predictable. The input functions are fine for computer generated input, less so for humans.

David.

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

Quote:
Why bother with fputc/fgetc
Because I want to use STDIO and fgetc, and filestreams are the only example I have to work with to interface to STDIO, otherwise I would have no problem. It's all about doing things proper you know. :)
Quote:
They also appreciate seeing the keys that they type.
That's not what happens when I type in a password anywhere. :) I want to get 1 character as it gets typed, unbuffered, from any input I may choose, the 2 USARTS in this case. The buffered stuff works well as written by Joerg for STDIN.

The problem I see with my code above is that I cannot receive 0x00 (Control Shift @). In ASM I just use the T bit to indicate the reception of a char. So I could make the variable used Global..big no no??.. and then return 0 or 1 depending on the reception of a char.

Old dogs should stick to old tricks?? :roll: I seem to get stuck in the stupidest, simplest things with C.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

John,

I gave an example function. You may wish to extend it to :

#include 

void fgetline(char buf[], unsigned char max, FILE *fp, char echo)
{
   char c, cnt = 0;
   while (cnt < max && (c = getc(fp)) != '\r') {
      if (c == '\b') {
         if (cnt == 0) continue;
         else cnt--;
      }
      else buf[cnt++] = c;
      if (echo == SECRET) putc('*', fp);
      if (echo == SHOW) putc(c, fp);
      if (echo == SILENT) ;    // no echo
   }
   buf[cnt] = 0;
}

Depending on your 'echo' parameter, you would do what suits you.

Yes. I am a great believer in using standard functions. You can achieve your desired behaviour in different ways.

A fundamental choice is whether you have a kbhit() and a getc() function.
Or whether you have a single getc() and forgo receiving a valid ascii NUL from the keyboard.

You can use a single getc() function if you return 16 bits rather than 8 bits. Use some magic 16-bit values for NO_KEY_PRESSED or FRAMING_ERROR or whatever.

Or you use the original UNIX approach. Set the terminal mode for echo, line-buffering etc.

David.

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

Some good ideas there. I have something similar in my ASM code where the "message in" routine looks at a flag

	sbr    msg_status,1<<pword_mode		;Set password mode for echo

if the bit is set then a * is echoed instead of the character.

Anyway the immediate problem is solved -1 value for now, I'll think a bit more about it when I have some more playtime.

Seeing the getc and fgetc are standard functions I thought the need for unbuffered chars would have been addressed already somewhere and it was just me not knowing what I'm doing that was causing the issue.

For some strange reason I always thought that getchar was buffered (and from stdin) while getc was not buffered and from other streams, half right.

I have some vague recollection of this behaviour under either CP/M or DOS.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

The buffering or otherwise gets a little complicated.

You either set a terminal into RAW mode, and handle all editing yourself.
Or you let the operating system look after the punter until she chooses to be finished.

CP/M or DOS were more likely to handle everything as RAW in the first place. I would guess that this is the most appropriate method for an AVR too.

The 'Terminal Monitor' of an Arduino environment gives the input to Java/Windoze and is a bit of a pain. i.e. it behaves differently to Hyperterminal.

I am impressed that you are succumbing to using standard C Library functions. This must be the ultimate sacrifice for a dedicated ASM programmer.

David.