Mark Mark - 3 months ago 21
C Question

Using printf with two UARTs

I have implemented

fputc
and
fgetc
in retarget.c to successfully use printf via UART0 on a Cortex-M3.

However, I want a second uart channel for additional debug information. How can I integrate this as nicely as I can UART0 using printf?

For example, using fprintf to a custom target and checking in
fputc
which target to send the character to..
E.g. for normal output
fprintf(UART0,"..");
and for debug output
fprintf(UART1,"..");


But I cannot see if fopen is called for stdout so I am struggling to see how to manually implement this. (If I just call
fprintf(RANDOM_VALUE,..)
, I don't know how this will behave.

I guess that once I have it directed to a different 'FILE', then it is simply a matter of checking which is being pointed to within fputc but it is the initial setting of the FILE pointer that I am struggling with.

Perhaps some way to differentiate between stdout and stderr, although then I still have the same problem for getting input from the two separate channels.

Also is fprintf in the microlib? If not, is there a better way to implement this?

Thanks!

Answer

fputc() takes a stream pointer argument, there are two standard output streams stdin, stdout and stderr. At the lower level of the retargeting these are associated with the file descriptors 0, 1, and 2 respectively, you can use this information to associate stderr with the alternate UART at the device driver level.

You can then output debug data using stderr thus:

fprintf (stderr, "Error reading file" ) ;

for example.

A minimal retargeting (specific to Keil ARM-MDK/RealView) might look like this:

struct __FILE 
{
    int handle;  
};

enum 
{
    STDIN_HANDLE,
    STDOUT_HANDLE,
    STDERR_HANDLE
} ;

FILE __stdin = {STDIN_HANDLE} ;
FILE __stdout = {STDOUT_HANDLE} ;
FILE __stderr = {STDERR_HANDLE} ;

int fputc(int ch, FILE *f) 
{
    int ret = EOF ;

    switch( f->handle )
    {
        case STDOUT_HANDLE :
            // Write character to UART0
            ...
            ret = ch ;
            break ;

        case STDERR_HANDLE :
            // Write character to UART1
            ...
            ret = ch ;
            break ;

        default :
            break ;

    return ret ;
}

Obviously this is also where you might hook in a filesystem if you needed, in which case your __FILE struct would no doubt have additional members.

If you don't want to use stderr for this purpose, you will have to retarget fopen() to translate a device name ("dbg:" for example) into a file descriptor for the desired port and then use stdio to output to the associated stream.

Also is fprintf in the microlib? If not, is there a better way to implement this?

The documentation will tell you, but yes. Microlib stdio support is controlled by the #pragma import(__use_full_stdio) directive, the documentation is not clear about what is excluded if this is not used. Try it without and use it if anything is missing. That said I would imagine that printf() is implemented as an fprintf() to the stdout stream, so if you have printf() you have fprintf().