<stdio.h> Input and Output

The header file stdio.h consists of types, macros and functions that provide support to perform input and output operations on files and streams. When a file is opened, it is associated with a stream. A stream is a pipeline for the flow of data into and out of files. Because different systems use different properties, the stream provides more uniform properties to allow reading and writing of the files.

Streams can be text streams or binary streams. Text streams consist of a sequence of characters divided into lines. Each line is terminated with a newline (‘\n’) character. The characters may be altered in their internal representation, particularly in regards to line endings. Binary streams consist of sequences of bytes of information. The bytes transmitted to the binary stream are not altered. There is no concept of lines - the file is just a series of bytes.

At start-up, three streams are automatically opened: stdin, stdout and stderr. stdin provides a stream for standard input, stdout is standard output and stderr is the standard error. Additional streams may be created with the fopen function. See fopen for the different types of file access that are permitted. These access types are used by fopen and freopen.

The type FILE is used to store information about each opened file stream. It includes such things as error indicators, end-of-file indicators, file position indicators and other internal status information needed to control a stream. Many functions in the stdio use FILE as an argument.

There are three types of buffering: unbuffered, line buffered and fully buffered. Unbuffered means a character or byte is transferred one at a time. Line buffered collects and transfers an entire line at a time (i.e., the newline character indicates the end of a line). Fully buffered allows blocks of an arbitrary size to be transmitted. The functions setbuf and setvbuf control file buffering.

The stdio.h file also contains functions that use input and output formats. The input formats, or scan formats, are used for reading data. Their descriptions can be found under scanf, but they are also used by fscanf and sscanf. The output formats, or print formats, are used for writing data. Their descriptions can be found under printf. These print formats are also used by fprintf, sprintf, vfprintf, vprintf and vsprintf.

Compiler Options

Certain compiler options may affect how standard I/O performs. In an effort to provide a more tailored version of the formatted I/O routines, the tool chain may convert a call to a printf or scanf style function to a different call. The options are summarized below:

Mixing modules compiled with these options may result in a larger executable size or incorrect execution if large and small double-sized data is shared across modules.

Customizing STDIO

The standard I/O relies on helper functions described in “Standard C Libraries - Support Functions.” These functions include read(), write(), open(), and close() which are called to read, write, open or close handles that are associated with standard I/O FILE pointers. The sources for these libraries are provided for you to customize as you wish.

It is recommended that these customized functions be allocated in a named section that begins with .lib. This will cause the linker to allocate them near to the rest of the library functions, which is where they ought to be.

The simplest way to redirect standard I/O to the peripheral of your choice is to select one of the default handles already in use. Also, you could open files with a specific name via fopen() by rewriting open() to return a new handle to be recognized by read() or write() as appropriate.

If only a specific peripheral is required, then you could associate handle 1 == stdout or 2 == stderr to another peripheral by writing the correct code to talk to the interested peripheral.

A complete generic solution might be:

/* should be in a header file */
enum my_handles {
    handle_stdin,
    handle_stdout,
    handle_stderr,
    handle_can1,
    handle_can2,
    handle_spi1,
    handle_spi2,
};

int _ ___attribute_ ___((_ ___weak_ ___, _ ___section_ ___(".libc")))
  open(const char *name, int access, int mode) {
     switch (name[0]) {
       case 'i' : return handle_stdin;
       case 'o' : return handle_stdout;
       case 'e' : return handle_stderr;
       case 'c' : return handle_can1;
       case 'C' : return handle_can2;
       case 's' : return handle_spi1;
       case 'S' : return handle_spi2;
       default: return handle_stderr;
    }
}

Single letters were used in this example because they are faster to check and use less memory. However, if memory is not an issue, you could use strcmp to compare full names.

In write(), you would write:

int __attribute__((__section__(".libc.write")))
  write(int handle, void *buffer, unsigned int len) {
     int i;
     volatile UxMODEBITS *umode = &U1MODEbits;
     volatile UxSTABITS *ustatus = &U1STAbits;
     volatile unsigned int *txreg = &U1TXREG;
     volatile unsigned int *brg = &U1BRG;

     switch (handle)
     {
     default:
     case 0:
     case 1:
     case 2:
        if ((_ ___C30_UART != 1) && (&U2BRG)) {
           umode = &U2MODEbits;
           ustatus = &U2STAbits;
           txreg = &U2TXREG;
           brg = &U2BRG;
        }
        if ((umode->UARTEN) == 0)
        {
           *brg = 0;
           umode->UARTEN = 1;
        }
        if ((ustatus->UTXEN) == 0)
        {
           ustatus->UTXEN = 1;
        }
        for (i = len; i; --i)
        {
           while ((ustatus->TRMT) ==0);
           *txreg = *(char*)buffer++;
        }
        break;
     case handle_can1:  /* code to support can1 */
        break;
     case handle_can2:  /* code to support can2 */
        break;
     case handle_spi1:  /* code to support spi1 */
        break;
     case handle_spi2:  /* code to support spi2 */
        break;
     }
     return(len);
}

where you would fill in the appropriate code as specified in the comments.

Now you can use the generic C STDIO features to write to another port:

FILE *can1 = fopen("c","w");
     fprintf(can1,"This will be output through the can\n");