Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 583
Mark Avatar asked Jan 23 '13 09:01

Mark


1 Answers

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().

like image 130
Clifford Avatar answered Sep 19 '22 00:09

Clifford