6.18.1.4 Customizing Helper Functions for XC32

The standard I/O relies on helper functions described in Syscall Interface. 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.

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_peripheral1,
    handle_peripheral2,
};

int
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_peripheral1;
        case 'C': return handle_peripheral2;
        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 write(int handle, void *buffer, unsigned int len) {
    int i;

    /* Do not try to output an empty string */
    if (!buffer || (len == 0))
        return 0;

    switch (handle) {
        case 0:
        case 1:
        case 2:
            for (i = len; i; --i) {
                /* place code here to write the next byte of buffer to the desired peripheral */
                PERIPH1 = *(char*) buffer;
                buffer++;
            }
            break;
        case handle_peripheral1:
            for (i = len; i; --i) {
                /* place code here to write the next byte of buffer to the desired peripheral */
                PERIPH2 = *(char*) buffer;
                buffer++;
            }
            break;
        case handle_peripheral2:
            /* place code here to write the buffer to the desired peripheral */
            break;

        default:
        {
            break;
        }
    }
    return (len);
}

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

Now you can use the generic C standard I/O features to write to another port:

#include <xc.h>
#include <stdio.h>

int main(void) {
    char my_string[] = "count";
    int count = 0;

    for (count = 0; count < 5; count++) {
        printf("%s: %d\n", my_string, count);
    }

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

    __builtin_software_breakpoint();
    while (1);
}