avr-libc and interrupt driven comms

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

avr-libc provides FILEs and stdio functionality through them. All the example code has things like this:

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

which is clearly polling the uart until the data register is empty...

how would you do interrupt driven serial communication with the avr-libc stdio library?

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

It is not avr-libc:s job to do anything, it is your job. Your code teaches what function to use when needed to write. It just happens to wait for UART.

So teach stdout to write to a buffer (ring buffer maybe) and trigger data transmission. Then, have the uart rx empty interrupt read from the buffer, until it is empty.

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

i thought the FILE structs were basically buffers. Am i missing something? I think so :P

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

Quote:

FILE structs were basically buffers

No they are "streams". There's no buffering it's just a communication channel. Basically your way of tying a Posix standard style file stream (stdout, stderr or parm to f*() functions) to a read or write byte routine that feeds the channel. It's your responsibility how data gets into/out of the channel and whether it's buffered and/or pollled/interrupting.

Cliff

PS As this is GCC specific that's where it's headed...

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

so my putchar and getchar routines should push and pop data in a circular buffer... and the ISRs do the same?

edit: yes, the ISRs do push and pop data. RXC should take data from the usart and put it in a buffer (using the putchar routine?) and the DRE ISR should pop from a buffer and write the uart's data register... right? :roll:

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

Suggest you look in the Tutorial Forum for abcminiuser (Dean)'s guide to Interrupt driven USART usage in which I think he gives a link to his own circular buffer routines. Then just plumb those into the UART_putchar()/UART_getchar() implementation that the FDEV stuff links to.

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

I've read it and i think i understand it. The trouble i'm having is how to deal with the FILEs. I have my own circular buffer implementation that works. I guess what i need to know is should I push and pop the buffers directly or use the stdio getc and putc?

This whole FILE framework is new to me and i confess i don't quite understand it

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

Try to put the FILE stuff out of your mind all together. In fact nothing says you HAVE to use it at all. In most C compilers users will implement UART_putchar() then UART_putstring() which basically just calls UART_putchar() until NUL. After that they'll implement a UART_put_int() which does an itoa() on a given integer then passes it to UART_putstring() which in turn passes it to UART_putchar(). All this is without any mention of files/streams.

The one thing the above does NOT allow is for you to use printf("the value is %03d", value) because printf() has no idea where to output. Should it be to UART or LCD or 7 seg display or SPI port or what?

All the file/stream stuff is doing for you is giving you a way to wire up printf() (and the other standard character outputting functions) to a routine that you provide. The mechanism by which this is done (FDEV_SETUP_STREAM) may all look a bit cumbersome but it kind of fits in with GCC's heritage from Linux/Unix systems where the notion of file streams is common to almost everything you do within GCC based operating systems and that same paradigm has merely be copied over for use on AVRs.

In other compilers the compiler author may well have already chosen to associate printf() and friends with a provided UART_putchar() routine from the C library. So the user can literally do:

int main(void) {
 init_UART();
 printf("hello");

and something sensible will occur. However, because the decisions already made to use a UART (usually UART0) polling routine you then have over-rides in such a compiler to say "don't do that, do this..." if you want to use a different UART or LCD output or whatever.

The difference in GCC is that no default output channel for printf() (etc.) is assumed and, instead, it's up to you to make the choice, implement the routines and finally tell GCC that these are the routines to be used for PRINTF by setting it up as a stream output and then associating that to stdout - the Unix standard stream that is used for terminal output.

But, like I say, you can ignore all this and just implement your UART stuff right up to UART_put_int() without worrying about files/streams/FDEV/stdout/printf()/et al

Cliff

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

The FILE structure just tells what (user-written) functions are used to write or read something.

So in your application, you seem to want printf to write to serial port using interrupts. In another application, someone else will want to write to HD44780 LCD with printf without interrupts.

Same thing with reading stuff from standard IO. You seem to want to read from serial port, someone might want to use attached PS/2 keyboard as input.

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

Thanks for this discussion. It is something that has intimidated me for a long time.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!